diff --git a/Makefile b/Makefile index f0e7f41..ab8c9ec 100644 --- a/Makefile +++ b/Makefile @@ -33,7 +33,7 @@ CLOCK = 16000000 PROGRAMMER ?= -c avrisp2 -P usb OBJECTS = main.o motion_control.o gcode.o spindle_control.o coolant_control.o serial.o \ protocol.o stepper.o eeprom.o settings.o planner.o nuts_bolts.o limits.o \ - print.o report.o + print.o probe.o report.o system.o # FUSES = -U hfuse:w:0xd9:m -U lfuse:w:0x24:m FUSES = -U hfuse:w:0xd2:m -U lfuse:w:0xff:m # update that line with this when programmer is back up: @@ -84,7 +84,7 @@ main.elf: $(OBJECTS) grbl.hex: main.elf rm -f grbl.hex avr-objcopy -j .text -j .data -O ihex main.elf grbl.hex - avr-size -C --mcu=$(DEVICE) main.elf + avr-size --format=berkeley main.elf # If you have an EEPROM section, you must also create a hex file for the # EEPROM and add it to the "flash" target. @@ -97,4 +97,3 @@ cpp: # include generated header dependencies -include $(OBJECTS:.o=.d) - diff --git a/README.md b/README.md index 660a778..119ef70 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,34 @@ -#Grbl - An embedded g-code interpreter and motion-controller for the Arduino/AVR328 microcontroller +#Grbl ------------ -Grbl is a no-compromise, high performance, low cost alternative to parallel-port-based motion control for CNC milling. It will run on a vanilla Arduino (Duemillanove/Uno) as long as it sports an Atmega 328. +This branch serves only as a developmental platform for working on new ideas that may eventually be installed into Grbl's edge branch. Please do not use as there is no guarantee this code base is up-to-date or working. -The controller is written in highly optimized C utilizing every clever feature of the AVR-chips to achieve precise timing and asynchronous operation. It is able to m aintain more than 30kHz of stable, jitter free control pulses. +------------ -It accepts standards-compliant G-code and has been tested with the output of several CAM tools with no problems. Arcs, circles and helical motion are fully supported, as well as, other basic functional g-code commands. Functions and variables are not currently supported, but may be included in future releases in a form of a pre-processor. +Grbl is a no-compromise, high performance, low cost alternative to parallel-port-based motion control for CNC milling. It will run on a vanilla Arduino (Duemillanove/Uno) as long as it sports an Atmega 328. (Other AVR CPUs are unofficially supported as well.) + +The controller is written in highly optimized C utilizing every clever feature of the AVR-chips to achieve precise timing and asynchronous operation. It is able to maintain up to 30kHz of stable, jitter free control pulses. + +It accepts standards-compliant G-code and has been tested with the output of several CAM tools with no problems. Arcs, circles and helical motion are fully supported, as well as, other basic functional g-code commands. Although canned cycles, functions, and variables are not currently supported (may in the future), GUIs can be built or modified easily to translate to straight g-code for Grbl. Grbl includes full acceleration management with look ahead. That means the controller will look up to 18 motions into the future and plan its velocities ahead to deliver smooth acceleration and jerk-free cornering. ##Changelog for v0.9 from v0.8 - **ALPHA status: Under heavy development.** - - New stepper algorithm: Based on the Pramod Ranade inverse time algorithm, but modified to ensure steps are executed exactly. This algorithm performs a constant timer tick and has a hard limit of 30kHz maximum step frequency. It is also highly tuneable and should be very easy to port to other microcontroller architectures. Overall, a much better, smoother stepper algorithm with the capability of very high speeds. - - Planner optimizations: Multiple changes to increase planner execution speed and removed redundant variables. + - New stepper algorithm: Complete overhaul of the handling of the stepper driver to simplify and reduce task time per ISR tick. Much smoother operation with the new Adaptive Multi-Axis Step Smoothing (AMASS) algorithm which does what its name implies. Users should audibly hear significant differences in how their machines move and see improved overall performance! + - Planner optimizations: Planning computations improved four-fold or more by optimizing end-to-end operations. Changes include streaming optimizations by ignoring already optimized blocks and removing redundant variables and computations and offloading them to the stepper algorithm to compute on an ad-hoc basis. + - Planner stability: Previous Grbl planners have all had a corruption issue in rare circumstances that becomes particularly problematic at high step frequencies and when jogging. The new planner is robust and incorruptible, meaning that we can fearlessly drive Grbl to it's highest limits. Combined with the new stepper algorithm and planner optimizations, this means 5x to 10x performance increases in our testing! This is all achieved through the introduction of an intermediary step segment buffer that "checks-out" steps from the planner buffer in real-time. - Acceleration independence: Each axes may be defined with different acceleration parameters and Grbl will automagically calculate the maximum acceleration through a path depending on the direction traveled. This is very useful for machine that have very different axes properties, like the ShapeOko z-axis. - Maximum velocity independence: As with acceleration, the maximum velocity of individual axes may be defined. All seek/rapids motions will move at these maximum rates, but never exceed any one axes. So, when two or more axes move, the limiting axis will move at its maximum rate, while the other axes are scaled down. - - Significantly improved arc performance: Arcs are now defined in terms of chordal tolerance, rather than segment length. Chordal tolerance will automatically scale all arc line segments depending on arc radius, such that the error does not exceed the tolerance value (default: 0.005 mm.) So, for larger radii arcs, Grbl can move faster through them, because the segments are always longer and the planner has more distance to plan with. + - Significantly improved arc performance: Arcs are now defined in terms of chordal tolerance, rather than segment length. Chordal tolerance will automatically scale all arc line segments depending on arc radius, such that the error does not exceed the tolerance value (default: 0.002 mm.) So, for larger radii arcs, Grbl can move faster through them, because the segments are always longer and the planner has more distance to plan with. - Soft limits: Checks if any motion command exceeds workspace limits. Alarms out when found. Another safety feature, but, unlike hard limits, position does not get lost, as it forces a feed hold before erroring out. + - CPU pin mapping: In an effort for Grbl to be compatible with other AVR architectures, such as the 1280 or 2560, a new cpu_map.h pin configuration file has been created to allow Grbl to be compiled for them. This is currently user supported, so your mileage may vary. If you run across a bug, please let us know or better send us a fix! Thanks in advance! - New Grbl SIMULATOR by @jgeisler: A completely independent wrapper of the Grbl main source code that may be compiled as an executable on a computer. No Arduino required. Simply simulates the responses of Grbl as if it was on an Arduino. May be used for many things: checking out how Grbl works, pre-process moves for GUI graphics, debugging of new features, etc. Much left to do, but potentially very powerful, as the dummy AVR variables can be written to output anything you need. - - Homing routine updated: Sets workspace volume in all negative space regardless of limit switch position. Common on pro CNCs. Also reduces soft limits CPU overhead. - - Feedrate overrides: In the works, but planner has begun to be re-factored for this feature. - - Jogging controls: Methodology needs to be to figured out first. Could be dropped due to flash space concerns. Last item on the agenda. + - Homing routine updated: Sets workspace volume in all negative space regardless of limit switch position. Common on pro CNCs. Now tied directly into the main planner and stepper modules to reduce flash space and allow maximum speeds during seeking. + - Combined limit pins capability: Limit switches can be combined to share the same pins to free up precious I/O pins for other purposes. When combined, users must adjust the homing cycle mask in config.h to not home the axes on a shared pin at the same time. + - Variable spindle speed output: Available only as a compile-time option through the config.h file. Enables PWM output for 'S' g-code commands. Enabling this feature will swap the Z-limit D11 pin and spindle enable D12 pin to access the hardware PWM on pin D12. The Z-limit pin, now on D12, should work just as it did before. + - Increased serial baud rate: Default serial baudrate is now 115200, because the new planner and stepper allows much higher processing and execution speeds that 9600 baud was just not cutting it. + - Feedrate overrides: (Slated for v1.0 release) The framework to enable feedrate overrides is in-place with v0.9, but the minor details has not yet been installed. + - Jogging controls: (Slated for v1.0 release) Methodology needs to be to figured out first. Could be dropped due to flash space concerns. _The project was initially inspired by the Arduino GCode Interpreter by Mike Ellery_ diff --git a/config.h b/config.h index d1587fa..2972d80 100644 --- a/config.h +++ b/config.h @@ -2,8 +2,8 @@ config.h - compile time configuration Part of Grbl + Copyright (c) 2011-2014 Sungeun K. Jeon Copyright (c) 2009-2011 Simen Svale Skogsrud - Copyright (c) 2011-2013 Sungeun K. Jeon Grbl is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -19,80 +19,24 @@ along with Grbl. If not, see . */ -#ifndef config_h -#define config_h +// This file contains compile-time configurations for Grbl's internal system. For the most part, +// users will not need to directly modify these, but they are here for specific needs, i.e. +// performance tuning or adjusting to non-typical machines. // IMPORTANT: Any changes here requires a full re-compiling of the source code to propagate them. +#ifndef config_h +#define config_h + // Default settings. Used when resetting EEPROM. Change to desired name in defaults.h #define DEFAULTS_GENERIC // Serial baud rate -#define BAUD_RATE 9600 +#define BAUD_RATE 115200 -// Define pin-assignments -// NOTE: All step bit and direction pins must be on the same port. -#define STEPPING_DDR DDRD -#define STEPPING_PORT PORTD -#define X_STEP_BIT 2 // Uno Digital Pin 2 -#define Y_STEP_BIT 3 // Uno Digital Pin 3 -#define Z_STEP_BIT 4 // Uno Digital Pin 4 -#define X_DIRECTION_BIT 5 // Uno Digital Pin 5 -#define Y_DIRECTION_BIT 6 // Uno Digital Pin 6 -#define Z_DIRECTION_BIT 7 // Uno Digital Pin 7 -#define STEP_MASK ((1<15kHz or so) and -// very high accelerations, this will reduce the error between how the planner plans the velocity -// profiles and how the stepper program actually performs them. The correct value for this parameter -// is machine dependent, so it's advised to set this only as high as needed. Approximate successful -// values can widely range from 50 to 200 or more. Cannot be greater than ISR_TICKS_PER_SECOND/2. -#define ACCELERATION_TICKS_PER_SECOND 120L +// Define the homing cycle patterns with bitmasks. The homing cycle first performs a search mode +// to quickly engage the limit switches, followed by a slower locate mode, and finished by a short +// pull-off motion to disengage the limit switches. The following HOMING_CYCLE_x defines are executed +// in order starting with suffix 0 and completes the homing routine for the specified-axes only. If +// an axis is omitted from the defines, it will not home, nor will the system update its position. +// Meaning that this allows for users with non-standard cartesian machines, such as a lathe (x then z, +// with no y), to configure the homing cycle behavior to their needs. +// NOTE: The homing cycle is designed to allow sharing of limit pins, if the axes are not in the same +// cycle, but this requires some pin settings changes in cpu_map.h file. For example, the default homing +// cycle can share the Z limit pin with either X or Y limit pins, since they are on different cycles. +// By sharing a pin, this frees up a precious IO pin for other purposes. In theory, all axes limit pins +// may be reduced to one pin, if all axes are homed with seperate cycles, or vice versa, all three axes +// on separate pin, but homed in one cycle. Also, it should be noted that the function of hard limits +// will not be affected by pin sharing. +// NOTE: Defaults are set for a traditional 3-axis CNC machine. Z-axis first to clear, followed by X & Y. +#define HOMING_CYCLE_0 (1< 3us, and, when added with the +// user-supplied step pulse time, the total time must not exceed 127us. Reported successful +// values for certain setups have ranged from 5 to 20us. +// #define STEP_PULSE_DELAY 10 // Step pulse delay in microseconds. Default disabled. // The number of linear motions in the planner buffer to be planned at any give time. The vast // majority of RAM that Grbl uses is based on this buffer size. Only increase if there is extra @@ -212,15 +188,23 @@ // up with planning new incoming motions as they are executed. // #define BLOCK_BUFFER_SIZE 18 // Uncomment to override default in planner.h. +// Governs the size of the intermediary step segment buffer between the step execution algorithm +// and the planner blocks. Each segment is set of steps executed at a constant velocity over a +// fixed time defined by ACCELERATION_TICKS_PER_SECOND. They are computed such that the planner +// block velocity profile is traced exactly. The size of this buffer governs how much step +// execution lead time there is for other Grbl processes have to compute and do their thing +// before having to come back and refill this buffer, currently at ~50msec of step moves. +// #define SEGMENT_BUFFER_SIZE 6 // Uncomment to override default in stepper.h. + // Line buffer size from the serial input stream to be executed. Also, governs the size of // each of the startup blocks, as they are each stored as a string of this size. Make sure // to account for the available EEPROM at the defined memory address in settings.h and for // the number of desired startup blocks. -// NOTE: 50 characters is not a problem except for extreme cases, but the line buffer size +// NOTE: 70 characters is not a problem except for extreme cases, but the line buffer size // can be too small and g-code blocks can get truncated. Officially, the g-code standards // support up to 256 characters. In future versions, this default will be increased, when // we know how much extra memory space we can re-invest into this. -// #define LINE_BUFFER_SIZE 50 // Uncomment to override default in protocol.h +// #define LINE_BUFFER_SIZE 70 // Uncomment to override default in protocol.h // Serial send and receive buffer size. The receive buffer is often used as another streaming // buffer to store incoming blocks to be processed by Grbl when its ready. Most streaming @@ -241,8 +225,25 @@ // case, please report any successes to grbl administrators! // #define ENABLE_XONXOFF // Default disabled. Uncomment to enable. +// A simple software debouncing feature for hard limit switches. When enabled, the interrupt +// monitoring the hard limit switch pins will enable the Arduino's watchdog timer to re-check +// the limit pin state after a delay of about 32msec. This can help with CNC machines with +// problematic false triggering of their hard limit switches, but it WILL NOT fix issues with +// electrical interference on the signal cables from external sources. It's recommended to first +// use shielded signal cables with their shielding connected to ground (old USB/computer cables +// work well and are cheap to find) and wire in a low-pass circuit into each limit pin. +// #define ENABLE_SOFTWARE_DEBOUNCE // Default disabled. Uncomment to enable. + // --------------------------------------------------------------------------------------- // TODO: Install compile-time option to send numeric status codes rather than strings. +// --------------------------------------------------------------------------------------- +// COMPILE-TIME ERROR CHECKING OF DEFINE VALUES: + +// #if (ISR_TICKS_PER_ACCELERATION_TICK > 255) +// #error Parameters ACCELERATION_TICKS / ISR_TICKS must be < 256 to prevent integer overflow. +// #endif + +// --------------------------------------------------------------------------------------- #endif diff --git a/coolant_control.c b/coolant_control.c index 8abd674..21fc106 100644 --- a/coolant_control.c +++ b/coolant_control.c @@ -2,7 +2,7 @@ coolant_control.c - coolant control methods Part of Grbl - Copyright (c) 2012 Sungeun K. Jeon + Copyright (c) 2012-2014 Sungeun K. Jeon Grbl is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -18,48 +18,46 @@ along with Grbl. If not, see . */ +#include "system.h" #include "coolant_control.h" -#include "settings.h" -#include "config.h" -#include "planner.h" +#include "protocol.h" +#include "gcode.h" -#include - -static uint8_t current_coolant_mode; void coolant_init() { - current_coolant_mode = COOLANT_DISABLE; - #if ENABLE_M7 + COOLANT_FLOOD_DDR |= (1 << COOLANT_FLOOD_BIT); + #ifdef ENABLE_M7 COOLANT_MIST_DDR |= (1 << COOLANT_MIST_BIT); #endif - COOLANT_FLOOD_DDR |= (1 << COOLANT_FLOOD_BIT); coolant_stop(); } + void coolant_stop() { + COOLANT_FLOOD_PORT &= ~(1 << COOLANT_FLOOD_BIT); #ifdef ENABLE_M7 COOLANT_MIST_PORT &= ~(1 << COOLANT_MIST_BIT); #endif - COOLANT_FLOOD_PORT &= ~(1 << COOLANT_FLOOD_BIT); } void coolant_run(uint8_t mode) { - if (mode != current_coolant_mode) - { - plan_synchronize(); // Ensure coolant turns on when specified in program. - if (mode == COOLANT_FLOOD_ENABLE) { - COOLANT_FLOOD_PORT |= (1 << COOLANT_FLOOD_BIT); - #ifdef ENABLE_M7 - } else if (mode == COOLANT_MIST_ENABLE) { - COOLANT_MIST_PORT |= (1 << COOLANT_MIST_BIT); - #endif - } else { - coolant_stop(); - } - current_coolant_mode = mode; + if (sys.state == STATE_CHECK_MODE) { return; } + + protocol_auto_cycle_start(); //temp fix for M8 lockup + protocol_buffer_synchronize(); // Ensure coolant turns on when specified in program. + if (mode == COOLANT_FLOOD_ENABLE) { + COOLANT_FLOOD_PORT |= (1 << COOLANT_FLOOD_BIT); + + #ifdef ENABLE_M7 + } else if (mode == COOLANT_MIST_ENABLE) { + COOLANT_MIST_PORT |= (1 << COOLANT_MIST_BIT); + #endif + + } else { + coolant_stop(); } } diff --git a/coolant_control.h b/coolant_control.h index fd2d549..83ce30b 100644 --- a/coolant_control.h +++ b/coolant_control.h @@ -2,7 +2,7 @@ coolant_control.h - spindle control methods Part of Grbl - Copyright (c) 2012 Sungeun K. Jeon + Copyright (c) 2012-2014 Sungeun K. Jeon Grbl is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -21,11 +21,6 @@ #ifndef coolant_control_h #define coolant_control_h -#include - -#define COOLANT_MIST_ENABLE 2 -#define COOLANT_FLOOD_ENABLE 1 -#define COOLANT_DISABLE 0 // Must be zero. void coolant_init(); void coolant_stop(); diff --git a/cpu_map.h b/cpu_map.h new file mode 100644 index 0000000..797f6bf --- /dev/null +++ b/cpu_map.h @@ -0,0 +1,264 @@ +/* + cpu_map.h - CPU and pin mapping configuration file + Part of Grbl + + Copyright (c) 2013-2014 Sungeun K. Jeon + + Grbl is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Grbl is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Grbl. If not, see . +*/ + +/* The cpu_map.h file serves as a central pin mapping settings file for different processor + types, i.e. AVR 328p or AVR Mega 2560. Grbl officially supports the Arduino Uno, but the + other supplied pin mappings are supplied by users, so your results may vary. */ + +// NOTE: This is still a work in progress. We are still centralizing the configurations to +// this file, so your success may vary for other CPUs. + +#ifndef cpu_map_h +#define cpu_map_h + +//---------------------------------------------------------------------------------------- + +#ifdef CPU_MAP_ATMEGA328P // (Arduino Uno) Officially supported by Grbl. + + // Define serial port pins and interrupt vectors. + #define SERIAL_RX USART_RX_vect + #define SERIAL_UDRE USART_UDRE_vect + + // Define step pulse output pins. NOTE: All step bit pins must be on the same port. + #define STEP_DDR DDRD + #define STEP_PORT PORTD + #define X_STEP_BIT 2 // Uno Digital Pin 2 + #define Y_STEP_BIT 3 // Uno Digital Pin 3 + #define Z_STEP_BIT 4 // Uno Digital Pin 4 + #define STEP_MASK ((1<, this feeds the simulator planner one line motion +% block. The left side is the first block in the buffer and the one that will be executed +% by the stepper module first. The right side is the end of the planner buffer, where the +% most recent streamed block is appended onto the planner buffer. Grbl's planner +% optimizes the velocity profiles between the beginning and end of the buffer based on +% the acceleration limits, intended velocity/feedrate, and line motion junction angles +% with their corresponding velocity limits (i.e. junctions with acute angles needs to come +% to a complete stop vs straight junctions can continue through at full speed.) + +% ---------------------------------------------------------------------------------------- + + +% Main function +% NOTE: This is just a way to keep all functions in one place, but all non-global variables +% are cleared as soon as this script completes. +function main() + +% Load pre-parsed gcode moves. +close all; +warning off; +clearvars -global +fid = fopen('matlab.gcode','r'); +gcode = textscan(fid,'%d8%f32%f32%f32%f32'); +nblock = length(gcode{1}); + +% Plot all g-code moves. +figure +line(gcode{3},gcode{4},gcode{5}); +axis equal; +% axis([min(gcode{3}) max(gcode{3}) min(gcode{4}) max(gcode{4}) min(gcode{5}) max(gcode{5})]); +title('G-code programming line motions'); +view(3); + +% Set up figure for planner queue +figure + +% Print help. +disp(''); +disp(' BLUE line indicates completed planner blocks that require no recalculation.'); +disp(' RED line indicates planner blocks that have been recalculated.'); +disp(' GREEN line indicates the location of the BPLANNED pointer. Always a recalculated block.'); +disp(' BLACK dotted-line and ''x'' indicates block nominal speed and max junction velocity, respectively.'); +disp(' CYAN ''.'' indicates block initial entry speed.'); + +% Define Grbl settings. +BUFFER_SIZE = 18; % Number of planner blocks in its ring buffer. +steps_per_mm = 200; +seekrate = 2500; % mm/min +acceleration = [100 100 100]; % mm/sec^2 [ X Y Z ] axes +junction_deviation = 0.1; % mm. See Grbl documentation on this parameter. +inch_2_mm = 25.4; +ACCELERATION_TICKS_PER_SECOND = 100; + +gcode{2} = gcode{2}; +gcode{2} = inch_2_mm*gcode{2}; +gcode{3} = inch_2_mm*gcode{3}; +gcode{4} = inch_2_mm*gcode{4}; +gcode{5} = inch_2_mm*gcode{5}; + +% Initialize blocks +block.steps = []; +block.step_event_count = []; +block.delta_mm = []; +block.millimeters = []; +block.acceleration = []; +block.speed = []; +block.nominal_speed = []; +block.max_entry_speed = []; +block.entry_speed = []; +block.recalculate_flag = false; +for i = 2:BUFFER_SIZE + block(i) = block(1); +end + +% Initialize planner +position = [0 0 0]; +prev_unit_vec = [0 0 0]; +previous_nominal_speed = 0; +pos = 0; + +% BHEAD and BTAIL act as pointers to the block head and tail. +% BPLANNED acts as a pointer of the location of the end of a completed/optimized plan. +bhead = 1; +btail = 1; +bplanned = 1; + +global block bhead btail bplanned nind acceleration BUFFER_SIZE pos ACCELERATION_TICKS_PER_SECOND + +% Main loop. Simulates plan_buffer_line(). All of the precalculations for the newest incoming +% block occurs here. Anything independent of the planner changes. +for i = 1:nblock + + target = round([gcode{3}(i) gcode{4}(i) gcode{5}(i)].*steps_per_mm); + if gcode{1}(i) == 1 + feedrate = gcode{2}(i); + else + feedrate = seekrate; + end + + nind = next_block_index(bhead); + if nind == btail + % Simulate a constantly full buffer. Move buffer tail. + bind = next_block_index(btail); + % Push planned pointer if encountered. Prevents it from looping back around the ring buffer. + if btail == bplanned; bplanned = bind; end + btail = bind; + end + + block(bhead).steps = abs(target-position); + block(bhead).step_event_count = max(block(bhead).steps); + + % Bail if this is a zero-length block + if block(bhead).step_event_count == 0 + disp(['Zero-length block in line ',int2str(i)]); + else + + % Compute path vector in terms of absolute step target and current positions + delta_mm = single((target-position)./steps_per_mm); + block(bhead).millimeters = single(norm(delta_mm)); + inverse_millimeters = single(1/block(bhead).millimeters); + + % Compute path unit vector + unit_vec = delta_mm/block(bhead).millimeters; + + % Calculate speed in mm/minute for each axis + inverse_minute = single(feedrate * inverse_millimeters); + block(bhead).speed = delta_mm*inverse_minute; + block(bhead).nominal_speed = block(bhead).millimeters*inverse_minute; + + % Calculate block acceleration. Operates on absolute value of unit vector. + [max_acc,ind] = max(abs(unit_vec)./acceleration); % Determine limiting acceleration + block(bhead).acceleration = acceleration(ind)/abs(unit_vec(ind)); + + % Compute maximum junction speed + block(bhead).max_entry_speed = 0.0; + if previous_nominal_speed > 0.0 + cos_theta = dot(-previous_unit_vec,unit_vec); + if (cos_theta < 0.95) + block(bhead).max_entry_speed = min([block(bhead).nominal_speed,previous_nominal_speed]); + if (cos_theta > -0.95) + sin_theta_d2 = sqrt(0.5*(1.0-cos_theta)); + block(bhead).max_entry_speed = min([block(bhead).max_entry_speed,sqrt(block(bhead).acceleration*3600*junction_deviation*sin_theta_d2/(1.0-sin_theta_d2))]); + end + end + end + + block(bhead).entry_speed = 0; % Just initialize. Set accurately in the replanning function. + block(bhead).recalculate_flag = true; % Plotting flag to indicate this block has been updated. + + previous_unit_vec = unit_vec; + previous_nominal_speed = block(bhead).nominal_speed; + position = target; + + bhead = nind; % Block complete. Push buffer pointer. + planner_recalculate(); + + plot_buffer_velocities(); + end +end +return + +% Computes the next block index in the planner ring buffer +function block_index = next_block_index(block_index) +global BUFFER_SIZE + block_index = block_index + 1; + if block_index > BUFFER_SIZE + block_index = 1; + end +return + +% Computes the previous block index in the planner ring buffer +function block_index = prev_block_index(block_index) +global BUFFER_SIZE + block_index = block_index-1; + if block_index < 1 + block_index = BUFFER_SIZE; + end +return + + +% Planner recalculate function. The magic happens here. +function planner_recalculate(block) + + global block bhead btail bplanned acceleration + + bind = prev_block_index(bhead); + if bind == bplanned; return; end % Bail, if only one block in buffer. Can't be operated on. + + % Reverse Pass: Coarsely maximize all possible deceleration curves back-planning from the last + % block in buffer. Cease planning when the last optimal planned or tail pointer is reached. + % NOTE: Forward pass will later refine and correct the reverse pass to create an optimal plan. + next = []; + curr = bind; % Last block in buffer. + + % Calculate maximum entry speed for last block in buffer, where the exit speed is always zero. + block(curr).entry_speed = min([block(curr).max_entry_speed,sqrt(2*block(curr).acceleration*60*60*block(curr).millimeters)]); + + bind = prev_block_index(bind); % Btail or second to last block + if (bind == bplanned) + % Only two plannable blocks in buffer. Reverse pass complete. + % Check if the first block is the tail. If so, notify stepper module to update its current parameters. + % if bind == btail; update_tail_block; end + else + % Three or more plannable blocks in buffer. Loop it. + while bind ~= bplanned % Loop until bplanned point hits. Replans to last plan point. + next = curr; + curr = bind; + bind = prev_block_index( bind ); % Previous block pointer. + + % Check if the first block is the tail. If so, notify stepper module to update its current parameters. + % if bind == btail; update_tail_block; end + + % Compute maximum entry speed decelerating over the current block from its exit speed. + if block(curr).entry_speed ~= block(curr).max_entry_speed + block(curr).recalculate_flag = true; % Plotting flag to indicate this block has been updated. + block(curr).entry_speed = min([ block(curr).max_entry_speed,... + sqrt(block(next).entry_speed^2 + 2*block(curr).acceleration*60*60*block(curr).millimeters)]); + end + + end + end + + % For two blocks, reverse pass is skipped, but forward pass plans second block entry speed + % onward. This prevents the first, or the potentially executing block, from being over-written. + % NOTE: Can never be bhead, since bsafe is always in active buffer. + next = bplanned; + bind = next_block_index(bplanned); % Start at bplanned + while bind ~= bhead + curr = next; + next = bind; + + % An acceleration block is always an optimally planned block since it starts from the first + % block's current speed or a maximum junction speed. Compute accelerations from this block + % and update the next block's entry speed. + if (block(curr).entry_speed < block(next).entry_speed) + % Once speed is set by forward planner, the plan for this block is finished and optimal. + % Increment the planner pointer forward one block. + + entry_speed = sqrt(block(curr).entry_speed^2 + 2*block(curr).acceleration*60*60*block(curr).millimeters); + if (block(next).entry_speed > entry_speed) + block(next).entry_speed = entry_speed; + bplanned = bind; + end + + end + + % Check if the next block entry speed is at max_entry_speed. If so, move the planned pointer, since + % this entry speed cannot be improved anymore and all prior blocks have been completed and optimally planned. + if block(next).entry_speed == block(next).max_entry_speed + bplanned = bind; + end + + % Recalculate trapezoid can be installed here, since it scans through all of the plannable blocks. + % NOTE: Eventually this will only be computed when being executed. + + bind = next_block_index( bind ); + + end + +return + +% ---------------------------------------------------------------------------------------- +% PLOTTING FUNCTIONS + +% Plots the entire buffer plan into a MATLAB figure to visual the plan. +% BLUE line indicates completed planner blocks that require no recalculation. +% RED line indicates planner blocks that have been recalculated. +% GREEN line indicates the location of the BPLANNED pointer. Always a recalculated block. +% BLACK dotted-line and 'x' indicates block nominal speed and max junction velocity, respectively. +% CYAN '.' indicates block initial entry speed. +function plot_buffer_velocities() + global block bhead btail bplanned acceleration pos ACCELERATION_TICKS_PER_SECOND + bind = btail; + curr = []; + next = []; + + pos_initial = 0; + pos = 0; + while bind ~= bhead + curr = next; + next = bind; + hold on; + if ~isempty(curr) + accel_d = estimate_acceleration_distance(block(curr).entry_speed, block(curr).nominal_speed, block(curr).acceleration*60*60); + decel_d = estimate_acceleration_distance(block(curr).nominal_speed, block(next).entry_speed,-block(curr).acceleration*60*60); + plateau_d = block(curr).millimeters-accel_d-decel_d; + if plateau_d < 0 + accel_d = intersection_distance(block(curr).entry_speed, block(next).entry_speed, block(curr).acceleration*60*60, block(curr).millimeters); + if accel_d < 0 + accel_d = 0; + elseif accel_d > block(curr).millimeters + accel_d = block(curr).millimeters; + end + plateau_d = 0; + end + color = 'b'; + if (block(curr).recalculate_flag || block(next).recalculate_flag) + block(curr).recalculate_flag = false; + color = 'r'; + end + if bplanned == curr + color = 'g'; + end + + plot_trap(pos,block(curr).entry_speed,block(next).entry_speed,block(curr).nominal_speed,block(curr).acceleration,accel_d,plateau_d,block(curr).millimeters,color) + plot([pos pos+block(curr).millimeters],block(curr).nominal_speed*[1 1],'k:') % BLACK dotted indicates + plot(pos,block(curr).max_entry_speed,'kx') + + pos = pos + block(curr).millimeters; + plot(pos,block(next).entry_speed,'c.'); + end + bind = next_block_index( bind ); + end + + accel_d = estimate_acceleration_distance(block(next).entry_speed, block(next).nominal_speed, block(next).acceleration*60*60); + decel_d = estimate_acceleration_distance(block(next).nominal_speed, 0, -block(next).acceleration*60*60); + plateau_d = block(next).millimeters-accel_d-decel_d; + if plateau_d < 0 + accel_d = intersection_distance(block(next).entry_speed, 0, block(next).acceleration*60*60, block(next).millimeters); + if accel_d < 0 + accel_d = 0; + elseif accel_d > block(next).millimeters + accel_d = block(next).millimeters; + end + plateau_d = 0; + end + block(next).recalculate_flag = false; + color = 'r'; + if bplanned == next + color= 'g'; + end + + plot_trap(pos,block(next).entry_speed,0,block(next).nominal_speed,block(next).acceleration,accel_d,plateau_d,block(next).millimeters,color) + plot([pos pos+block(next).millimeters],block(next).nominal_speed*[1 1],'k:') + plot(pos,block(next).max_entry_speed,'kx') + + plot(pos,block(next).entry_speed,'.'); + pos = pos + block(next).millimeters; + plot(pos,0,'rx'); + xlabel('mm'); + ylabel('mm/sec'); + xlim([pos_initial pos]) + title('Planner buffer optimized velocity profile'); + pause(); + hold off; + + plot(pos,0) +return + + +function d_a = estimate_acceleration_distance(initial_rate, target_rate, acceleration,rate_delta) + d_a = (target_rate*target_rate-initial_rate*initial_rate)/(2*acceleration); +return + +function d_i = intersection_distance(initial_rate, final_rate, acceleration, distance, rate_delta) + d_i = (2*acceleration*distance-initial_rate*initial_rate+final_rate*final_rate)/(4*acceleration); +return + + +% Simply plots the ac/de-celeration curves and plateaus of a trapezoid. +function plot_trap(pos,initial_rate,final_rate,rate,accel,accel_d,plateau_d,millimeters,color) + + dx = 1.0; % Line segment length + linex = [pos]; liney = [initial_rate]; + + % Acceleration + np = floor(accel_d/dx); + if np + v = initial_rate; + for i = 1:np + v = sqrt(v^2+2*accel*60*60*dx); + linex = [linex pos+i*dx]; + liney = [liney v]; + end + end + + % Plateau + v = sqrt(initial_rate^2 + 2*accel*60*60*accel_d); + if v < rate + rate = v; + end + linex = [linex pos+[accel_d accel_d+plateau_d]]; + liney = [liney [rate rate]]; + + % Deceleration + np = floor((millimeters-accel_d-plateau_d)/dx); + if np + v = rate; + for i = 1:np + v = sqrt(v^2-2*accel*60*60*dx); + linex = [linex pos+i*dx+accel_d+plateau_d]; + liney = [liney v]; + end + end + + linex = [linex pos+millimeters]; + liney = [ liney final_rate]; + plot(linex,liney,color); + +return + + + diff --git a/doc/test/matlab.gcode b/doc/test/matlab.gcode new file mode 100644 index 0000000..8ad9cdc --- /dev/null +++ b/doc/test/matlab.gcode @@ -0,0 +1,2362 @@ +0 0.0 0.0 0.0 0.0 +0 0.0 0.0 0.0 0.0 +0 0.0 0.0 0.0 0.0 +0 0.0 0.0 0.0 6.0 +0 0.0 37.56 12.33 6.0 +1 300.0 37.56 12.33 -1.0 +1 300.0 37.56 0.88 -1.0 +1 300.0 49.01 0.88 -1.0 +1 300.0 49.01 12.33 -1.0 +1 300.0 37.56 12.33 -1.0 +0 300.0 37.56 12.33 6.0 +0 300.0 37.56 0.88 6.0 +1 300.0 37.56 0.88 -1.0 +1 300.0 37.56 -10.57 -1.0 +1 300.0 49.01 -10.57 -1.0 +1 300.0 49.01 0.88 -1.0 +1 300.0 37.56 0.88 -1.0 +0 300.0 37.56 0.88 6.0 +0 300.0 49.01 12.33 6.0 +1 300.0 49.01 12.33 -1.0 +1 300.0 52.08 15.01 -1.0 +0 300.0 52.08 15.01 6.0 +0 300.0 49.01 0.88 6.0 +1 300.0 49.01 0.88 -1.0 +1 300.0 52.08 6.21 -1.0 +1 300.0 52.08 15.01 -1.0 +1 300.0 43.29 15.01 -1.0 +1 300.0 37.56 12.33 -1.0 +0 300.0 37.56 12.33 6.0 +0 300.0 49.01 -10.57 6.0 +1 300.0 49.01 -10.57 -1.0 +1 300.0 52.08 -2.58 -1.0 +1 300.0 52.08 6.21 -1.0 +1 300.0 49.01 0.88 -1.0 +0 300.0 49.01 0.88 6.0 +0 300.0 49.01 0.88 20.0 +0 300.0 0.0 0.0 20.0 +1 300.0 -7.1 -39.88 20.0 +1 300.0 -10.3 -38.12 20.0 +1 300.0 -10.7 -37.9 20.0 +1 300.0 -11.1 -37.68 20.0 +1 300.0 -11.89 -37.23 20.0 +1 300.0 -13.45 -36.36 20.0 +1 300.0 -13.82 -36.14 20.0 +1 300.0 -14.2 -35.92 20.0 +1 300.0 -14.92 -35.48 20.0 +1 300.0 -16.29 -34.63 20.0 +1 300.0 -16.64 -34.4 20.0 +1 300.0 -16.97 -34.16 20.0 +1 300.0 -17.61 -33.71 20.0 +1 300.0 -17.91 -33.49 20.0 +1 300.0 -18.2 -33.26 20.0 +1 300.0 -18.74 -32.82 20.0 +1 300.0 -18.99 -32.6 20.0 +1 300.0 -19.23 -32.39 20.0 +1 300.0 -19.45 -32.17 20.0 +1 300.0 -19.65 -31.96 20.0 +1 300.0 -19.84 -31.75 20.0 +1 300.0 -20.02 -31.54 20.0 +1 300.0 -20.18 -31.34 20.0 +1 300.0 -20.32 -31.13 20.0 +1 300.0 -20.45 -30.93 20.0 +1 300.0 -20.56 -30.73 20.0 +1 300.0 -20.65 -30.54 20.0 +1 300.0 -20.73 -30.34 20.0 +1 300.0 -20.79 -30.15 20.0 +1 300.0 -20.84 -29.96 20.0 +1 300.0 -20.87 -29.78 20.0 +1 300.0 -20.88 -29.59 20.0 +1 300.0 -20.88 -29.41 20.0 +1 300.0 -20.85 -29.24 20.0 +1 300.0 -20.82 -29.06 20.0 +1 300.0 -20.76 -28.89 20.0 +1 300.0 -20.7 -28.72 20.0 +1 300.0 -20.61 -28.56 20.0 +1 300.0 -20.51 -28.4 20.0 +1 300.0 -20.39 -28.24 20.0 +1 300.0 -20.26 -28.08 20.0 +1 300.0 -20.12 -27.93 20.0 +1 300.0 -19.96 -27.78 20.0 +1 300.0 -19.78 -27.64 20.0 +1 300.0 -19.59 -27.5 20.0 +1 300.0 -19.39 -27.36 20.0 +1 300.0 -19.18 -27.23 20.0 +1 300.0 -18.95 -27.09 20.0 +1 300.0 -18.71 -26.97 20.0 +1 300.0 -18.46 -26.84 20.0 +1 300.0 -18.2 -26.73 20.0 +1 300.0 -17.92 -26.61 20.0 +1 300.0 -17.64 -26.5 20.0 +1 300.0 -17.35 -26.39 20.0 +1 300.0 -16.74 -26.18 20.0 +1 300.0 -16.42 -26.09 20.0 +1 300.0 -16.09 -25.99 20.0 +1 300.0 -15.76 -25.9 20.0 +1 300.0 -15.43 -25.82 20.0 +1 300.0 -15.08 -25.74 20.0 +1 300.0 -14.74 -25.66 20.0 +1 300.0 -14.03 -25.52 20.0 +1 300.0 -13.67 -25.45 20.0 +1 300.0 -13.31 -25.39 20.0 +1 300.0 -12.95 -25.33 20.0 +1 300.0 -12.59 -25.28 20.0 +1 300.0 -12.23 -25.23 20.0 +1 300.0 -11.87 -25.18 20.0 +1 300.0 -11.51 -25.14 20.0 +1 300.0 -11.15 -25.1 20.0 +1 300.0 -10.82 -25.07 20.0 +1 300.0 -10.5 -25.04 20.0 +1 300.0 -10.17 -25.02 20.0 +1 300.0 -9.85 -25.0 20.0 +1 300.0 -9.54 -24.98 20.0 +1 300.0 -9.23 -24.96 20.0 +1 300.0 -8.92 -24.95 20.0 +1 300.0 -8.63 -24.94 20.0 +1 300.0 -8.33 -24.94 20.0 +1 300.0 -8.05 -24.94 20.0 +1 300.0 -7.77 -24.94 20.0 +1 300.0 -7.5 -24.95 20.0 +1 300.0 -7.24 -24.95 20.0 +1 300.0 -6.99 -24.97 20.0 +1 300.0 -6.75 -24.98 20.0 +1 300.0 -6.52 -25.0 20.0 +1 300.0 -6.29 -25.02 20.0 +1 300.0 -6.08 -25.05 20.0 +1 300.0 -5.88 -25.07 20.0 +1 300.0 -5.69 -25.11 20.0 +1 300.0 -5.52 -25.14 20.0 +1 300.0 -5.35 -25.18 20.0 +1 300.0 -5.2 -25.22 20.0 +1 300.0 -5.06 -25.26 20.0 +1 300.0 -4.93 -25.31 20.0 +1 300.0 -4.81 -25.36 20.0 +1 300.0 -4.71 -25.41 20.0 +1 300.0 -4.62 -25.46 20.0 +1 300.0 -4.55 -25.52 20.0 +1 300.0 -4.49 -25.58 20.0 +1 300.0 -4.44 -25.64 20.0 +1 300.0 -4.41 -25.71 20.0 +1 300.0 -4.39 -25.77 20.0 +1 300.0 -4.38 -25.84 20.0 +1 300.0 -4.39 -25.92 20.0 +1 300.0 -4.42 -25.99 20.0 +1 300.0 -4.45 -26.07 20.0 +1 300.0 -4.51 -26.15 20.0 +1 300.0 -4.57 -26.23 20.0 +1 300.0 -4.65 -26.32 20.0 +1 300.0 -4.75 -26.41 20.0 +1 300.0 -4.86 -26.5 20.0 +1 300.0 -4.98 -26.59 20.0 +1 300.0 -5.12 -26.68 20.0 +1 300.0 -5.27 -26.78 20.0 +1 300.0 -5.43 -26.87 20.0 +1 300.0 -5.8 -27.07 20.0 +1 300.0 -6.0 -27.18 20.0 +1 300.0 -6.22 -27.28 20.0 +1 300.0 -6.68 -27.5 20.0 +1 300.0 -7.75 -27.94 20.0 +1 300.0 -8.05 -28.06 20.0 +1 300.0 -8.35 -28.17 20.0 +1 300.0 -8.99 -28.41 20.0 +1 300.0 -10.36 -28.89 20.0 +1 300.0 -10.75 -29.02 20.0 +1 300.0 -11.15 -29.16 20.0 +1 300.0 -11.97 -29.43 20.0 +1 300.0 -13.66 -29.97 20.0 +1 300.0 -17.11 -31.07 20.0 +1 300.0 -17.54 -31.2 20.0 +1 300.0 -17.96 -31.34 20.0 +1 300.0 -18.79 -31.6 20.0 +1 300.0 -20.37 -32.13 20.0 +1 300.0 -20.75 -32.26 20.0 +1 300.0 -21.11 -32.38 20.0 +1 300.0 -21.82 -32.63 20.0 +1 300.0 -23.09 -33.11 20.0 +1 300.0 -23.38 -33.23 20.0 +1 300.0 -23.66 -33.34 20.0 +1 300.0 -24.16 -33.56 20.0 +1 300.0 -24.4 -33.67 20.0 +1 300.0 -24.61 -33.77 20.0 +1 300.0 -25.0 -33.97 20.0 +1 300.0 -25.18 -34.07 20.0 +1 300.0 -25.33 -34.17 20.0 +1 300.0 -25.47 -34.26 20.0 +1 300.0 -25.59 -34.35 20.0 +1 300.0 -25.7 -34.44 20.0 +1 300.0 -25.79 -34.52 20.0 +1 300.0 -25.86 -34.6 20.0 +1 300.0 -25.92 -34.68 20.0 +1 300.0 -25.96 -34.75 20.0 +1 300.0 -25.98 -34.83 20.0 +1 300.0 -25.99 -34.9 20.0 +1 300.0 -25.98 -34.96 20.0 +1 300.0 -25.95 -35.02 20.0 +1 300.0 -25.9 -35.08 20.0 +1 300.0 -25.84 -35.14 20.0 +1 300.0 -25.76 -35.19 20.0 +1 300.0 -25.67 -35.24 20.0 +1 300.0 -25.56 -35.28 20.0 +1 300.0 -25.43 -35.33 20.0 +1 300.0 -25.29 -35.36 20.0 +1 300.0 -25.13 -35.4 20.0 +1 300.0 -24.96 -35.43 20.0 +1 300.0 -24.78 -35.45 20.0 +1 300.0 -24.58 -35.48 20.0 +1 300.0 -24.37 -35.5 20.0 +1 300.0 -24.14 -35.51 20.0 +1 300.0 -23.91 -35.52 20.0 +1 300.0 -23.66 -35.53 20.0 +1 300.0 -23.4 -35.53 20.0 +1 300.0 -23.13 -35.53 20.0 +1 300.0 -22.85 -35.53 20.0 +1 300.0 -22.56 -35.52 20.0 +1 300.0 -22.26 -35.51 20.0 +1 300.0 -21.95 -35.49 20.0 +1 300.0 -21.64 -35.47 20.0 +1 300.0 -21.31 -35.45 20.0 +1 300.0 -20.98 -35.42 20.0 +1 300.0 -20.64 -35.38 20.0 +1 300.0 -20.3 -35.35 20.0 +1 300.0 -19.95 -35.31 20.0 +1 300.0 -19.59 -35.26 20.0 +1 300.0 -19.24 -35.21 20.0 +1 300.0 -18.88 -35.16 20.0 +1 300.0 -18.51 -35.1 20.0 +1 300.0 -18.14 -35.04 20.0 +1 300.0 -17.78 -34.97 20.0 +1 300.0 -17.04 -34.83 20.0 +1 300.0 -16.67 -34.75 20.0 +1 300.0 -16.31 -34.67 20.0 +1 300.0 -15.58 -34.49 20.0 +1 300.0 -15.22 -34.4 20.0 +1 300.0 -14.87 -34.3 20.0 +1 300.0 -14.17 -34.09 20.0 +1 300.0 -13.83 -33.98 20.0 +1 300.0 -13.5 -33.86 20.0 +1 300.0 -13.18 -33.74 20.0 +1 300.0 -12.86 -33.62 20.0 +1 300.0 -12.55 -33.49 20.0 +1 300.0 -12.25 -33.36 20.0 +1 300.0 -11.68 -33.08 20.0 +1 300.0 -11.4 -32.94 20.0 +1 300.0 -11.14 -32.79 20.0 +1 300.0 -10.9 -32.64 20.0 +1 300.0 -10.66 -32.48 20.0 +1 300.0 -10.44 -32.33 20.0 +1 300.0 -10.22 -32.16 20.0 +1 300.0 -10.03 -32.0 20.0 +1 300.0 -9.84 -31.83 20.0 +1 300.0 -9.67 -31.65 20.0 +1 300.0 -9.51 -31.48 20.0 +1 300.0 -9.37 -31.3 20.0 +1 300.0 -9.24 -31.11 20.0 +1 300.0 -9.13 -30.92 20.0 +1 300.0 -9.03 -30.73 20.0 +1 300.0 -8.95 -30.54 20.0 +1 300.0 -8.88 -30.34 20.0 +1 300.0 -8.84 -30.14 20.0 +1 300.0 -8.8 -29.93 20.0 +1 300.0 -8.78 -29.73 20.0 +1 300.0 -8.78 -29.52 20.0 +1 300.0 -8.79 -29.3 20.0 +1 300.0 -8.82 -29.09 20.0 +1 300.0 -8.87 -28.87 20.0 +1 300.0 -8.93 -28.64 20.0 +1 300.0 -9.01 -28.43 20.0 +1 300.0 -9.09 -28.22 20.0 +1 300.0 -9.19 -28.01 20.0 +1 300.0 -9.31 -27.79 20.0 +1 300.0 -9.44 -27.57 20.0 +1 300.0 -9.58 -27.35 20.0 +1 300.0 -9.9 -26.9 20.0 +1 300.0 -10.08 -26.68 20.0 +1 300.0 -10.27 -26.45 20.0 +1 300.0 -10.69 -25.98 20.0 +1 300.0 -10.92 -25.75 20.0 +1 300.0 -11.16 -25.51 20.0 +1 300.0 -11.68 -25.03 20.0 +1 300.0 -11.95 -24.79 20.0 +1 300.0 -12.23 -24.55 20.0 +1 300.0 -12.82 -24.06 20.0 +1 300.0 -14.11 -23.07 20.0 +1 300.0 -17.0 -21.04 20.0 +1 300.0 -17.38 -20.78 20.0 +1 300.0 -17.76 -20.52 20.0 +1 300.0 -18.53 -20.01 20.0 +1 300.0 -20.08 -18.98 20.0 +1 300.0 -20.46 -18.72 20.0 +1 300.0 -20.84 -18.46 20.0 +1 300.0 -21.6 -17.95 20.0 +1 300.0 -23.07 -16.93 20.0 +1 300.0 -23.43 -16.67 20.0 +1 300.0 -23.78 -16.42 20.0 +1 300.0 -24.45 -15.91 20.0 +1 300.0 -25.72 -14.91 20.0 +1 300.0 -26.01 -14.66 20.0 +1 300.0 -26.3 -14.42 20.0 +1 300.0 -26.83 -13.93 20.0 +1 300.0 -27.09 -13.69 20.0 +1 300.0 -27.33 -13.45 20.0 +1 300.0 -27.78 -12.97 20.0 +1 300.0 -28.0 -12.71 20.0 +1 300.0 -28.21 -12.46 20.0 +1 300.0 -28.4 -12.21 20.0 +1 300.0 -28.58 -11.96 20.0 +1 300.0 -28.75 -11.71 20.0 +1 300.0 -28.89 -11.47 20.0 +1 300.0 -29.03 -11.23 20.0 +1 300.0 -29.14 -10.99 20.0 +1 300.0 -29.24 -10.75 20.0 +1 300.0 -29.32 -10.51 20.0 +1 300.0 -29.39 -10.28 20.0 +1 300.0 -29.44 -10.05 20.0 +1 300.0 -29.48 -9.82 20.0 +1 300.0 -29.49 -9.6 20.0 +1 300.0 -29.49 -9.38 20.0 +1 300.0 -29.48 -9.16 20.0 +1 300.0 -29.44 -8.95 20.0 +1 300.0 -29.39 -8.73 20.0 +1 300.0 -29.32 -8.53 20.0 +1 300.0 -29.24 -8.32 20.0 +1 300.0 -29.14 -8.12 20.0 +1 300.0 -29.03 -7.92 20.0 +1 300.0 -28.9 -7.72 20.0 +1 300.0 -28.75 -7.53 20.0 +1 300.0 -28.59 -7.34 20.0 +1 300.0 -28.42 -7.16 20.0 +1 300.0 -28.23 -6.97 20.0 +1 300.0 -28.02 -6.8 20.0 +1 300.0 -27.8 -6.62 20.0 +1 300.0 -27.57 -6.45 20.0 +1 300.0 -27.07 -6.12 20.0 +1 300.0 -26.8 -5.96 20.0 +1 300.0 -26.52 -5.8 20.0 +1 300.0 -25.93 -5.5 20.0 +1 300.0 -25.62 -5.36 20.0 +1 300.0 -25.3 -5.22 20.0 +1 300.0 -24.63 -4.95 20.0 +1 300.0 -24.28 -4.82 20.0 +1 300.0 -23.93 -4.69 20.0 +1 300.0 -23.21 -4.46 20.0 +1 300.0 -22.84 -4.34 20.0 +1 300.0 -22.46 -4.24 20.0 +1 300.0 -21.71 -4.03 20.0 +1 300.0 -21.32 -3.94 20.0 +1 300.0 -20.94 -3.84 20.0 +1 300.0 -20.17 -3.67 20.0 +1 300.0 -19.79 -3.59 20.0 +1 300.0 -19.4 -3.52 20.0 +1 300.0 -18.64 -3.38 20.0 +1 300.0 -18.27 -3.31 20.0 +1 300.0 -17.89 -3.25 20.0 +1 300.0 -17.53 -3.2 20.0 +1 300.0 -17.16 -3.15 20.0 +1 300.0 -16.81 -3.1 20.0 +1 300.0 -16.46 -3.06 20.0 +1 300.0 -16.12 -3.02 20.0 +1 300.0 -15.78 -2.98 20.0 +1 300.0 -15.48 -2.95 20.0 +1 300.0 -15.18 -2.92 20.0 +1 300.0 -14.89 -2.9 20.0 +1 300.0 -14.61 -2.88 20.0 +1 300.0 -14.34 -2.87 20.0 +1 300.0 -14.08 -2.85 20.0 +1 300.0 -13.83 -2.85 20.0 +1 300.0 -13.59 -2.84 20.0 +1 300.0 -13.36 -2.84 20.0 +1 300.0 -13.14 -2.84 20.0 +1 300.0 -12.93 -2.84 20.0 +1 300.0 -12.73 -2.85 20.0 +1 300.0 -12.55 -2.86 20.0 +1 300.0 -12.38 -2.87 20.0 +1 300.0 -12.22 -2.89 20.0 +1 300.0 -12.07 -2.91 20.0 +1 300.0 -11.94 -2.93 20.0 +1 300.0 -11.82 -2.96 20.0 +1 300.0 -11.72 -2.98 20.0 +1 300.0 -11.63 -3.01 20.0 +1 300.0 -11.55 -3.05 20.0 +1 300.0 -11.48 -3.08 20.0 +1 300.0 -11.44 -3.12 20.0 +1 300.0 -11.4 -3.17 20.0 +1 300.0 -11.38 -3.21 20.0 +1 300.0 -11.37 -3.26 20.0 +1 300.0 -11.38 -3.31 20.0 +1 300.0 -11.4 -3.36 20.0 +1 300.0 -11.44 -3.42 20.0 +1 300.0 -11.49 -3.47 20.0 +1 300.0 -11.56 -3.53 20.0 +1 300.0 -11.64 -3.59 20.0 +1 300.0 -11.73 -3.66 20.0 +1 300.0 -11.84 -3.72 20.0 +1 300.0 -11.96 -3.79 20.0 +1 300.0 -12.09 -3.86 20.0 +1 300.0 -12.24 -3.94 20.0 +1 300.0 -12.4 -4.01 20.0 +1 300.0 -12.77 -4.17 20.0 +1 300.0 -12.97 -4.25 20.0 +1 300.0 -13.18 -4.33 20.0 +1 300.0 -13.64 -4.5 20.0 +1 300.0 -14.69 -4.85 20.0 +1 300.0 -14.98 -4.94 20.0 +1 300.0 -15.28 -5.04 20.0 +1 300.0 -15.9 -5.22 20.0 +1 300.0 -17.23 -5.62 20.0 +1 300.0 -20.15 -6.43 20.0 +1 300.0 -20.52 -6.53 20.0 +1 300.0 -20.89 -6.63 20.0 +1 300.0 -21.63 -6.84 20.0 +1 300.0 -23.11 -7.24 20.0 +1 300.0 -25.89 -8.03 20.0 +1 300.0 -26.22 -8.12 20.0 +1 300.0 -26.54 -8.22 20.0 +1 300.0 -27.14 -8.4 20.0 +1 300.0 -28.26 -8.76 20.0 +1 300.0 -28.51 -8.85 20.0 +1 300.0 -28.76 -8.93 20.0 +1 300.0 -29.21 -9.1 20.0 +1 300.0 -29.43 -9.18 20.0 +1 300.0 -29.62 -9.25 20.0 +1 300.0 -29.99 -9.4 20.0 +1 300.0 -30.15 -9.48 20.0 +1 300.0 -30.3 -9.55 20.0 +1 300.0 -30.43 -9.62 20.0 +1 300.0 -30.56 -9.68 20.0 +1 300.0 -30.67 -9.75 20.0 +1 300.0 -30.77 -9.81 20.0 +1 300.0 -30.85 -9.87 20.0 +1 300.0 -30.92 -9.93 20.0 +1 300.0 -30.97 -9.99 20.0 +1 300.0 -31.02 -10.04 20.0 +1 300.0 -31.04 -10.09 20.0 +1 300.0 -31.06 -10.14 20.0 +1 300.0 -31.06 -10.19 20.0 +1 300.0 -31.04 -10.23 20.0 +1 300.0 -31.02 -10.27 20.0 +1 300.0 -30.97 -10.31 20.0 +1 300.0 -30.92 -10.35 20.0 +1 300.0 -30.85 -10.38 20.0 +1 300.0 -30.76 -10.41 20.0 +1 300.0 -30.67 -10.44 20.0 +1 300.0 -30.56 -10.47 20.0 +1 300.0 -30.43 -10.49 20.0 +1 300.0 -30.3 -10.51 20.0 +1 300.0 -30.15 -10.53 20.0 +1 300.0 -29.98 -10.54 20.0 +1 300.0 -29.81 -10.55 20.0 +1 300.0 -29.62 -10.56 20.0 +1 300.0 -29.42 -10.56 20.0 +1 300.0 -29.21 -10.56 20.0 +1 300.0 -28.99 -10.56 20.0 +1 300.0 -28.76 -10.56 20.0 +1 300.0 -28.52 -10.55 20.0 +1 300.0 -28.24 -10.54 20.0 +1 300.0 -27.95 -10.52 20.0 +1 300.0 -27.66 -10.5 20.0 +1 300.0 -27.35 -10.48 20.0 +1 300.0 -27.03 -10.45 20.0 +1 300.0 -26.7 -10.42 20.0 +1 300.0 -26.37 -10.38 20.0 +1 300.0 -26.02 -10.34 20.0 +1 300.0 -25.67 -10.3 20.0 +1 300.0 -25.31 -10.25 20.0 +1 300.0 -24.57 -10.15 20.0 +1 300.0 -24.19 -10.09 20.0 +1 300.0 -23.81 -10.02 20.0 +1 300.0 -23.03 -9.88 20.0 +1 300.0 -22.64 -9.81 20.0 +1 300.0 -22.25 -9.73 20.0 +1 300.0 -21.45 -9.56 20.0 +1 300.0 -21.06 -9.46 20.0 +1 300.0 -20.66 -9.37 20.0 +1 300.0 -19.88 -9.16 20.0 +1 300.0 -19.49 -9.05 20.0 +1 300.0 -19.1 -8.94 20.0 +1 300.0 -18.34 -8.7 20.0 +1 300.0 -17.97 -8.58 20.0 +1 300.0 -17.61 -8.45 20.0 +1 300.0 -16.9 -8.18 20.0 +1 300.0 -16.55 -8.04 20.0 +1 300.0 -16.22 -7.89 20.0 +1 300.0 -15.57 -7.59 20.0 +1 300.0 -15.27 -7.44 20.0 +1 300.0 -14.97 -7.28 20.0 +1 300.0 -14.41 -6.95 20.0 +1 300.0 -14.15 -6.77 20.0 +1 300.0 -13.9 -6.6 20.0 +1 300.0 -13.67 -6.42 20.0 +1 300.0 -13.45 -6.24 20.0 +1 300.0 -13.24 -6.05 20.0 +1 300.0 -13.04 -5.86 20.0 +1 300.0 -12.86 -5.67 20.0 +1 300.0 -12.7 -5.47 20.0 +1 300.0 -12.55 -5.27 20.0 +1 300.0 -12.41 -5.07 20.0 +1 300.0 -12.29 -4.86 20.0 +1 300.0 -12.19 -4.65 20.0 +1 300.0 -12.1 -4.44 20.0 +1 300.0 -12.03 -4.22 20.0 +1 300.0 -11.97 -4.0 20.0 +1 300.0 -11.94 -3.78 20.0 +1 300.0 -11.91 -3.56 20.0 +1 300.0 -11.9 -3.33 20.0 +1 300.0 -11.91 -3.1 20.0 +1 300.0 -11.94 -2.86 20.0 +1 300.0 -11.98 -2.63 20.0 +1 300.0 -12.04 -2.39 20.0 +1 300.0 -12.11 -2.14 20.0 +1 300.0 -12.2 -1.9 20.0 +1 300.0 -12.3 -1.67 20.0 +1 300.0 -12.41 -1.44 20.0 +1 300.0 -12.54 -1.2 20.0 +1 300.0 -12.68 -0.96 20.0 +1 300.0 -12.83 -0.73 20.0 +1 300.0 -12.99 -0.49 20.0 +1 300.0 -13.36 0.0 20.0 +1 300.0 -13.55 0.25 20.0 +1 300.0 -13.77 0.49 20.0 +1 300.0 -14.23 0.99 20.0 +1 300.0 -15.27 2.01 20.0 +1 300.0 -15.55 2.27 20.0 +1 300.0 -15.84 2.52 20.0 +1 300.0 -16.45 3.04 20.0 +1 300.0 -17.75 4.09 20.0 +1 300.0 -20.57 6.22 20.0 +1 300.0 -20.93 6.49 20.0 +1 300.0 -21.3 6.75 20.0 +1 300.0 -22.02 7.29 20.0 +1 300.0 -23.46 8.35 20.0 +1 300.0 -23.81 8.62 20.0 +1 300.0 -24.15 8.88 20.0 +1 300.0 -24.84 9.41 20.0 +1 300.0 -26.13 10.46 20.0 +1 300.0 -26.43 10.72 20.0 +1 300.0 -26.73 10.97 20.0 +1 300.0 -27.3 11.49 20.0 +1 300.0 -27.57 11.74 20.0 +1 300.0 -27.83 12.0 20.0 +1 300.0 -28.32 12.5 20.0 +1 300.0 -28.55 12.75 20.0 +1 300.0 -28.77 13.0 20.0 +1 300.0 -28.98 13.24 20.0 +1 300.0 -29.17 13.49 20.0 +1 300.0 -29.36 13.73 20.0 +1 300.0 -29.53 13.97 20.0 +1 300.0 -29.69 14.21 20.0 +1 300.0 -29.83 14.45 20.0 +1 300.0 -29.97 14.7 20.0 +1 300.0 -30.1 14.95 20.0 +1 300.0 -30.21 15.2 20.0 +1 300.0 -30.31 15.45 20.0 +1 300.0 -30.39 15.69 20.0 +1 300.0 -30.45 15.94 20.0 +1 300.0 -30.49 16.18 20.0 +1 300.0 -30.52 16.41 20.0 +1 300.0 -30.53 16.64 20.0 +1 300.0 -30.53 16.87 20.0 +1 300.0 -30.5 17.1 20.0 +1 300.0 -30.47 17.33 20.0 +1 300.0 -30.41 17.55 20.0 +1 300.0 -30.34 17.77 20.0 +1 300.0 -30.25 17.98 20.0 +1 300.0 -30.14 18.19 20.0 +1 300.0 -30.02 18.4 20.0 +1 300.0 -29.88 18.6 20.0 +1 300.0 -29.73 18.8 20.0 +1 300.0 -29.56 19.0 20.0 +1 300.0 -29.38 19.2 20.0 +1 300.0 -29.18 19.39 20.0 +1 300.0 -28.96 19.57 20.0 +1 300.0 -28.73 19.76 20.0 +1 300.0 -28.49 19.93 20.0 +1 300.0 -28.23 20.11 20.0 +1 300.0 -27.96 20.28 20.0 +1 300.0 -27.68 20.45 20.0 +1 300.0 -27.39 20.61 20.0 +1 300.0 -27.08 20.77 20.0 +1 300.0 -26.43 21.08 20.0 +1 300.0 -26.1 21.23 20.0 +1 300.0 -25.75 21.37 20.0 +1 300.0 -25.02 21.65 20.0 +1 300.0 -24.65 21.78 20.0 +1 300.0 -24.27 21.91 20.0 +1 300.0 -23.49 22.15 20.0 +1 300.0 -23.09 22.26 20.0 +1 300.0 -22.69 22.38 20.0 +1 300.0 -21.88 22.58 20.0 +1 300.0 -21.47 22.68 20.0 +1 300.0 -21.05 22.77 20.0 +1 300.0 -20.22 22.95 20.0 +1 300.0 -19.81 23.03 20.0 +1 300.0 -19.4 23.11 20.0 +1 300.0 -18.57 23.25 20.0 +1 300.0 -18.17 23.31 20.0 +1 300.0 -17.77 23.37 20.0 +1 300.0 -16.98 23.48 20.0 +1 300.0 -16.59 23.52 20.0 +1 300.0 -16.21 23.57 20.0 +1 300.0 -15.84 23.61 20.0 +1 300.0 -15.47 23.64 20.0 +1 300.0 -15.12 23.67 20.0 +1 300.0 -14.77 23.7 20.0 +1 300.0 -14.43 23.72 20.0 +1 300.0 -14.1 23.74 20.0 +1 300.0 -13.79 23.75 20.0 +1 300.0 -13.49 23.76 20.0 +1 300.0 -13.2 23.77 20.0 +1 300.0 -12.92 23.77 20.0 +1 300.0 -12.66 23.77 20.0 +1 300.0 -12.4 23.77 20.0 +1 300.0 -12.16 23.76 20.0 +1 300.0 -11.94 23.75 20.0 +1 300.0 -11.72 23.73 20.0 +1 300.0 -11.53 23.71 20.0 +1 300.0 -11.34 23.69 20.0 +1 300.0 -11.17 23.66 20.0 +1 300.0 -11.02 23.63 20.0 +1 300.0 -10.88 23.6 20.0 +1 300.0 -10.75 23.56 20.0 +1 300.0 -10.64 23.52 20.0 +1 300.0 -10.55 23.48 20.0 +1 300.0 -10.47 23.43 20.0 +1 300.0 -10.41 23.39 20.0 +1 300.0 -10.37 23.33 20.0 +1 300.0 -10.34 23.28 20.0 +1 300.0 -10.32 23.22 20.0 +1 300.0 -10.33 23.16 20.0 +1 300.0 -10.34 23.1 20.0 +1 300.0 -10.38 23.03 20.0 +1 300.0 -10.43 22.96 20.0 +1 300.0 -10.5 22.89 20.0 +1 300.0 -10.58 22.82 20.0 +1 300.0 -10.68 22.74 20.0 +1 300.0 -10.79 22.66 20.0 +1 300.0 -10.92 22.58 20.0 +1 300.0 -11.06 22.49 20.0 +1 300.0 -11.22 22.41 20.0 +1 300.0 -11.39 22.32 20.0 +1 300.0 -11.77 22.14 20.0 +1 300.0 -11.98 22.04 20.0 +1 300.0 -12.21 21.95 20.0 +1 300.0 -12.7 21.75 20.0 +1 300.0 -12.96 21.64 20.0 +1 300.0 -13.23 21.54 20.0 +1 300.0 -13.81 21.33 20.0 +1 300.0 -15.07 20.89 20.0 +1 300.0 -15.4 20.78 20.0 +1 300.0 -15.74 20.67 20.0 +1 300.0 -16.44 20.44 20.0 +1 300.0 -17.9 19.98 20.0 +1 300.0 -20.87 19.04 20.0 +1 300.0 -21.21 18.93 20.0 +1 300.0 -21.55 18.82 20.0 +1 300.0 -22.21 18.6 20.0 +1 300.0 -23.47 18.17 20.0 +1 300.0 -23.77 18.07 20.0 +1 300.0 -24.07 17.96 20.0 +1 300.0 -24.63 17.76 20.0 +1 300.0 -25.65 17.36 20.0 +1 300.0 -25.88 17.27 20.0 +1 300.0 -26.1 17.17 20.0 +1 300.0 -26.51 16.98 20.0 +1 300.0 -26.7 16.89 20.0 +1 300.0 -26.87 16.8 20.0 +1 300.0 -27.18 16.63 20.0 +1 300.0 -27.32 16.55 20.0 +1 300.0 -27.45 16.46 20.0 +1 300.0 -27.56 16.39 20.0 +1 300.0 -27.66 16.31 20.0 +1 300.0 -27.74 16.23 20.0 +1 300.0 -27.82 16.16 20.0 +1 300.0 -27.87 16.08 20.0 +1 300.0 -27.92 16.01 20.0 +1 300.0 -27.95 15.95 20.0 +1 300.0 -27.96 15.88 20.0 +1 300.0 -27.96 15.82 20.0 +1 300.0 -27.95 15.76 20.0 +1 300.0 -27.93 15.7 20.0 +1 300.0 -27.89 15.64 20.0 +1 300.0 -27.83 15.59 20.0 +1 300.0 -27.76 15.54 20.0 +1 300.0 -27.68 15.49 20.0 +1 300.0 -27.59 15.44 20.0 +1 300.0 -27.48 15.4 20.0 +1 300.0 -27.35 15.36 20.0 +1 300.0 -27.21 15.32 20.0 +1 300.0 -27.06 15.29 20.0 +1 300.0 -26.9 15.25 20.0 +1 300.0 -26.72 15.22 20.0 +1 300.0 -26.53 15.2 20.0 +1 300.0 -26.33 15.17 20.0 +1 300.0 -26.12 15.15 20.0 +1 300.0 -25.89 15.14 20.0 +1 300.0 -25.65 15.12 20.0 +1 300.0 -25.41 15.11 20.0 +1 300.0 -25.14 15.1 20.0 +1 300.0 -24.88 15.1 20.0 +1 300.0 -24.59 15.1 20.0 +1 300.0 -24.3 15.1 20.0 +1 300.0 -24.0 15.1 20.0 +1 300.0 -23.69 15.11 20.0 +1 300.0 -23.38 15.12 20.0 +1 300.0 -23.05 15.13 20.0 +1 300.0 -22.72 15.15 20.0 +1 300.0 -22.38 15.17 20.0 +1 300.0 -22.0 15.2 20.0 +1 300.0 -21.61 15.23 20.0 +1 300.0 -21.22 15.26 20.0 +1 300.0 -20.82 15.3 20.0 +1 300.0 -20.42 15.34 20.0 +1 300.0 -20.01 15.38 20.0 +1 300.0 -19.18 15.49 20.0 +1 300.0 -18.76 15.55 20.0 +1 300.0 -18.34 15.61 20.0 +1 300.0 -17.49 15.74 20.0 +1 300.0 -17.07 15.82 20.0 +1 300.0 -16.64 15.9 20.0 +1 300.0 -15.8 16.06 20.0 +1 300.0 -15.38 16.15 20.0 +1 300.0 -14.97 16.25 20.0 +1 300.0 -14.15 16.45 20.0 +1 300.0 -13.75 16.55 20.0 +1 300.0 -13.35 16.66 20.0 +1 300.0 -12.58 16.89 20.0 +1 300.0 -12.21 17.02 20.0 +1 300.0 -11.84 17.14 20.0 +1 300.0 -11.14 17.41 20.0 +1 300.0 -10.8 17.54 20.0 +1 300.0 -10.48 17.68 20.0 +1 300.0 -9.86 17.98 20.0 +1 300.0 -9.57 18.13 20.0 +1 300.0 -9.29 18.28 20.0 +1 300.0 -9.02 18.44 20.0 +1 300.0 -8.77 18.6 20.0 +1 300.0 -8.53 18.77 20.0 +1 300.0 -8.3 18.94 20.0 +1 300.0 -8.09 19.11 20.0 +1 300.0 -7.9 19.29 20.0 +1 300.0 -7.72 19.47 20.0 +1 300.0 -7.55 19.65 20.0 +1 300.0 -7.4 19.84 20.0 +1 300.0 -7.27 20.03 20.0 +1 300.0 -7.15 20.22 20.0 +1 300.0 -7.05 20.42 20.0 +1 300.0 -6.96 20.62 20.0 +1 300.0 -6.89 20.82 20.0 +1 300.0 -6.84 21.02 20.0 +1 300.0 -6.81 21.23 20.0 +1 300.0 -6.79 21.44 20.0 +1 300.0 -6.78 21.65 20.0 +1 300.0 -6.8 21.87 20.0 +1 300.0 -6.83 22.09 20.0 +1 300.0 -6.87 22.31 20.0 +1 300.0 -6.93 22.53 20.0 +1 300.0 -7.01 22.76 20.0 +1 300.0 -7.1 22.99 20.0 +1 300.0 -7.21 23.22 20.0 +1 300.0 -7.34 23.45 20.0 +1 300.0 -7.48 23.68 20.0 +1 300.0 -7.63 23.92 20.0 +1 300.0 -7.98 24.4 20.0 +1 300.0 -8.16 24.63 20.0 +1 300.0 -8.36 24.86 20.0 +1 300.0 -8.78 25.32 20.0 +1 300.0 -9.75 26.25 20.0 +1 300.0 -10.02 26.49 20.0 +1 300.0 -10.29 26.73 20.0 +1 300.0 -10.86 27.2 20.0 +1 300.0 -12.09 28.17 20.0 +1 300.0 -12.41 28.41 20.0 +1 300.0 -12.73 28.66 20.0 +1 300.0 -13.39 29.14 20.0 +1 300.0 -14.75 30.12 20.0 +1 300.0 -17.46 32.08 20.0 +1 300.0 -17.79 32.32 20.0 +1 300.0 -18.11 32.56 20.0 +1 300.0 -18.74 33.04 20.0 +1 300.0 -19.93 33.99 20.0 +1 300.0 -20.21 34.23 20.0 +1 300.0 -20.48 34.46 20.0 +1 300.0 -21.0 34.93 20.0 +1 300.0 -21.24 35.16 20.0 +1 300.0 -21.47 35.39 20.0 +1 300.0 -21.91 35.84 20.0 +1 300.0 -22.11 36.06 20.0 +1 300.0 -22.3 36.28 20.0 +1 300.0 -22.48 36.51 20.0 +1 300.0 -22.65 36.72 20.0 +1 300.0 -22.8 36.94 20.0 +1 300.0 -22.94 37.16 20.0 +1 300.0 -23.07 37.37 20.0 +1 300.0 -23.18 37.58 20.0 +1 300.0 -23.29 37.79 20.0 +1 300.0 -23.37 37.99 20.0 +1 300.0 -23.45 38.2 20.0 +1 300.0 -23.51 38.4 20.0 +1 300.0 -23.55 38.6 20.0 +1 300.0 -23.59 38.8 20.0 +1 300.0 -23.6 38.99 20.0 +1 300.0 -23.61 39.18 20.0 +1 300.0 -23.59 39.39 20.0 +1 300.0 -23.56 39.59 20.0 +1 300.0 -23.52 39.79 20.0 +1 300.0 -23.45 39.98 20.0 +1 300.0 -23.37 40.17 20.0 +1 300.0 -23.27 40.36 20.0 +1 300.0 -23.16 40.55 20.0 +1 300.0 -23.03 40.73 20.0 +1 300.0 -22.88 40.91 20.0 +1 300.0 -22.72 41.09 20.0 +1 300.0 -22.54 41.26 20.0 +1 300.0 -22.34 41.42 20.0 +1 300.0 -22.13 41.59 20.0 +1 300.0 -21.91 41.75 20.0 +1 300.0 -21.67 41.91 20.0 +1 300.0 -21.41 42.06 20.0 +1 300.0 -21.14 42.21 20.0 +1 300.0 -20.86 42.35 20.0 +1 300.0 -20.57 42.49 20.0 +1 300.0 -20.26 42.63 20.0 +1 300.0 -19.94 42.76 20.0 +1 300.0 -19.6 42.89 20.0 +1 300.0 -18.91 43.14 20.0 +1 300.0 -18.54 43.25 20.0 +1 300.0 -18.17 43.37 20.0 +1 300.0 -17.79 43.47 20.0 +1 300.0 -17.39 43.58 20.0 +1 300.0 -17.0 43.68 20.0 +1 300.0 -16.59 43.77 20.0 +1 300.0 -15.76 43.95 20.0 +1 300.0 -15.34 44.03 20.0 +1 300.0 -14.91 44.11 20.0 +1 300.0 -14.48 44.18 20.0 +1 300.0 -14.04 44.25 20.0 +1 300.0 -13.61 44.32 20.0 +1 300.0 -13.17 44.38 20.0 +1 300.0 -12.29 44.49 20.0 +1 300.0 -11.85 44.53 20.0 +1 300.0 -11.41 44.58 20.0 +1 300.0 -10.98 44.62 20.0 +1 300.0 -10.55 44.65 20.0 +1 300.0 -10.12 44.68 20.0 +1 300.0 -9.69 44.7 20.0 +1 300.0 -9.27 44.73 20.0 +1 300.0 -8.86 44.74 20.0 +1 300.0 -8.45 44.76 20.0 +1 300.0 -8.04 44.76 20.0 +1 300.0 -7.65 44.77 20.0 +1 300.0 -7.26 44.77 20.0 +1 300.0 -6.88 44.76 20.0 +1 300.0 -6.52 44.75 20.0 +1 300.0 -6.16 44.74 20.0 +1 300.0 -5.81 44.72 20.0 +1 300.0 -5.47 44.7 20.0 +1 300.0 -5.14 44.68 20.0 +1 300.0 -4.83 44.65 20.0 +1 300.0 -4.53 44.61 20.0 +1 300.0 -4.24 44.58 20.0 +1 300.0 -3.96 44.53 20.0 +1 300.0 -3.7 44.49 20.0 +1 300.0 -3.46 44.44 20.0 +1 300.0 -3.23 44.39 20.0 +1 300.0 -3.01 44.33 20.0 +1 300.0 -2.81 44.27 20.0 +1 300.0 -2.63 44.21 20.0 +1 300.0 -2.46 44.14 20.0 +1 300.0 -2.31 44.07 20.0 +1 300.0 -2.17 44.0 20.0 +1 300.0 -2.04 43.92 20.0 +1 300.0 -1.94 43.84 20.0 +1 300.0 -1.85 43.76 20.0 +1 300.0 -1.77 43.67 20.0 +1 300.0 -1.72 43.59 20.0 +1 300.0 -1.67 43.49 20.0 +1 300.0 -1.65 43.4 20.0 +1 300.0 -1.64 43.3 20.0 +1 300.0 -1.65 43.2 20.0 +1 300.0 -1.67 43.09 20.0 +1 300.0 -1.71 42.99 20.0 +1 300.0 -1.76 42.88 20.0 +1 300.0 -1.83 42.77 20.0 +1 300.0 -1.92 42.65 20.0 +1 300.0 -2.02 42.53 20.0 +1 300.0 -2.13 42.41 20.0 +1 300.0 -2.27 42.29 20.0 +1 300.0 -2.41 42.17 20.0 +1 300.0 -2.57 42.04 20.0 +1 300.0 -2.93 41.78 20.0 +1 300.0 -3.13 41.65 20.0 +1 300.0 -3.35 41.51 20.0 +1 300.0 -3.81 41.23 20.0 +1 300.0 -4.06 41.09 20.0 +1 300.0 -4.32 40.95 20.0 +1 300.0 -4.87 40.66 20.0 +1 300.0 -6.08 40.07 20.0 +1 300.0 -8.79 38.84 20.0 +1 300.0 -9.14 38.68 20.0 +1 300.0 -9.49 38.53 20.0 +1 300.0 -10.21 38.21 20.0 +1 300.0 -11.61 37.58 20.0 +1 300.0 -14.21 36.34 20.0 +1 300.0 -14.49 36.2 20.0 +1 300.0 -14.76 36.06 20.0 +1 300.0 -15.26 35.78 20.0 +1 300.0 -15.5 35.65 20.0 +1 300.0 -15.73 35.51 20.0 +1 300.0 -16.16 35.24 20.0 +1 300.0 -16.36 35.11 20.0 +1 300.0 -16.55 34.98 20.0 +1 300.0 -16.73 34.84 20.0 +1 300.0 -16.89 34.72 20.0 +1 300.0 -17.05 34.59 20.0 +1 300.0 -17.19 34.47 20.0 +1 300.0 -17.32 34.34 20.0 +1 300.0 -17.43 34.22 20.0 +1 300.0 -17.54 34.1 20.0 +1 300.0 -17.63 33.98 20.0 +1 300.0 -17.7 33.87 20.0 +1 300.0 -17.76 33.75 20.0 +1 300.0 -17.81 33.64 20.0 +1 300.0 -17.85 33.53 20.0 +1 300.0 -17.87 33.42 20.0 +1 300.0 -17.88 33.31 20.0 +1 300.0 -17.87 33.21 20.0 +1 300.0 -17.84 33.11 20.0 +1 300.0 -17.81 33.01 20.0 +1 300.0 -17.76 32.91 20.0 +1 300.0 -17.7 32.82 20.0 +1 300.0 -17.62 32.73 20.0 +1 300.0 -17.52 32.64 20.0 +1 300.0 -17.42 32.55 20.0 +1 300.0 -17.3 32.47 20.0 +1 300.0 -17.16 32.39 20.0 +1 300.0 -17.01 32.31 20.0 +1 300.0 -16.85 32.23 20.0 +1 300.0 -16.68 32.16 20.0 +1 300.0 -16.49 32.09 20.0 +1 300.0 -16.29 32.02 20.0 +1 300.0 -16.07 31.96 20.0 +1 300.0 -15.85 31.89 20.0 +1 300.0 -15.61 31.84 20.0 +1 300.0 -15.36 31.78 20.0 +1 300.0 -15.1 31.73 20.0 +1 300.0 -14.83 31.68 20.0 +1 300.0 -14.54 31.63 20.0 +1 300.0 -14.25 31.59 20.0 +1 300.0 -13.95 31.55 20.0 +1 300.0 -13.63 31.51 20.0 +1 300.0 -13.31 31.48 20.0 +1 300.0 -12.98 31.44 20.0 +1 300.0 -12.64 31.42 20.0 +1 300.0 -12.29 31.39 20.0 +1 300.0 -11.93 31.37 20.0 +1 300.0 -11.57 31.35 20.0 +1 300.0 -11.2 31.34 20.0 +1 300.0 -10.82 31.33 20.0 +1 300.0 -10.44 31.32 20.0 +1 300.0 -10.06 31.31 20.0 +1 300.0 -9.67 31.31 20.0 +1 300.0 -9.27 31.32 20.0 +1 300.0 -8.87 31.32 20.0 +1 300.0 -8.47 31.33 20.0 +1 300.0 -8.07 31.34 20.0 +1 300.0 -7.63 31.36 20.0 +1 300.0 -7.19 31.38 20.0 +1 300.0 -6.75 31.4 20.0 +1 300.0 -6.3 31.43 20.0 +1 300.0 -5.86 31.47 20.0 +1 300.0 -5.42 31.5 20.0 +1 300.0 -4.98 31.54 20.0 +1 300.0 -4.55 31.59 20.0 +1 300.0 -4.11 31.64 20.0 +1 300.0 -3.68 31.69 20.0 +1 300.0 -3.26 31.75 20.0 +1 300.0 -2.84 31.81 20.0 +1 300.0 -2.43 31.87 20.0 +1 300.0 -2.02 31.94 20.0 +1 300.0 -1.23 32.09 20.0 +1 300.0 -0.85 32.17 20.0 +1 300.0 -0.47 32.26 20.0 +1 300.0 -0.11 32.34 20.0 +1 300.0 0.24 32.43 20.0 +1 300.0 0.59 32.53 20.0 +1 300.0 0.92 32.63 20.0 +1 300.0 1.55 32.84 20.0 +1 300.0 1.84 32.95 20.0 +1 300.0 2.12 33.06 20.0 +1 300.0 2.39 33.18 20.0 +1 300.0 2.65 33.3 20.0 +1 300.0 2.89 33.42 20.0 +1 300.0 3.11 33.55 20.0 +1 300.0 3.32 33.68 20.0 +1 300.0 3.52 33.81 20.0 +1 300.0 3.7 33.95 20.0 +1 300.0 3.86 34.09 20.0 +1 300.0 4.01 34.24 20.0 +1 300.0 4.14 34.38 20.0 +1 300.0 4.26 34.53 20.0 +1 300.0 4.36 34.69 20.0 +1 300.0 4.44 34.84 20.0 +1 300.0 4.5 35.0 20.0 +1 300.0 4.55 35.16 20.0 +1 300.0 4.59 35.33 20.0 +1 300.0 4.6 35.49 20.0 +1 300.0 4.6 35.66 20.0 +1 300.0 4.58 35.83 20.0 +1 300.0 4.55 36.01 20.0 +1 300.0 4.5 36.19 20.0 +1 300.0 4.43 36.36 20.0 +1 300.0 4.35 36.55 20.0 +1 300.0 4.25 36.73 20.0 +1 300.0 4.14 36.92 20.0 +1 300.0 4.01 37.1 20.0 +1 300.0 3.87 37.29 20.0 +1 300.0 3.71 37.49 20.0 +1 300.0 3.36 37.88 20.0 +1 300.0 3.16 38.07 20.0 +1 300.0 2.94 38.27 20.0 +1 300.0 2.48 38.67 20.0 +1 300.0 1.43 39.5 20.0 +1 300.0 1.16 39.69 20.0 +1 300.0 0.88 39.88 20.0 +1 300.0 0.3 40.28 20.0 +1 300.0 -0.93 41.07 20.0 +1 300.0 -3.54 42.67 20.0 +1 300.0 -3.87 42.87 20.0 +1 300.0 -4.2 43.07 20.0 +1 300.0 -4.86 43.46 20.0 +1 300.0 -6.14 44.25 20.0 +1 300.0 -6.44 44.45 20.0 +1 300.0 -6.75 44.64 20.0 +1 300.0 -7.34 45.03 20.0 +1 300.0 -8.43 45.79 20.0 +1 300.0 -8.68 45.98 20.0 +1 300.0 -8.93 46.16 20.0 +1 300.0 -9.38 46.53 20.0 +1 300.0 -9.6 46.71 20.0 +1 300.0 -9.8 46.89 20.0 +1 300.0 -10.17 47.24 20.0 +1 300.0 -10.34 47.41 20.0 +1 300.0 -10.49 47.59 20.0 +1 300.0 -10.64 47.76 20.0 +1 300.0 -10.77 47.92 20.0 +1 300.0 -10.88 48.09 20.0 +1 300.0 -10.99 48.26 20.0 +1 300.0 -11.08 48.42 20.0 +1 300.0 -11.15 48.58 20.0 +1 300.0 -11.22 48.73 20.0 +1 300.0 -11.27 48.89 20.0 +1 300.0 -11.3 49.04 20.0 +1 300.0 -11.32 49.19 20.0 +1 300.0 -11.33 49.34 20.0 +1 300.0 -11.32 49.48 20.0 +1 300.0 -11.3 49.62 20.0 +1 300.0 -11.26 49.76 20.0 +1 300.0 -11.21 49.9 20.0 +1 300.0 -11.14 50.03 20.0 +1 300.0 -11.06 50.16 20.0 +1 300.0 -10.96 50.29 20.0 +1 300.0 -10.85 50.42 20.0 +1 300.0 -10.73 50.54 20.0 +1 300.0 -10.59 50.66 20.0 +1 300.0 -10.44 50.77 20.0 +1 300.0 -10.28 50.89 20.0 +1 300.0 -10.1 50.99 20.0 +1 300.0 -9.91 51.1 20.0 +1 300.0 -9.71 51.2 20.0 +1 300.0 -9.5 51.3 20.0 +1 300.0 -9.27 51.39 20.0 +1 300.0 -8.78 51.57 20.0 +1 300.0 -8.52 51.66 20.0 +1 300.0 -8.25 51.74 20.0 +1 300.0 -7.97 51.82 20.0 +1 300.0 -7.67 51.9 20.0 +1 300.0 -7.37 51.97 20.0 +1 300.0 -7.06 52.04 20.0 +1 300.0 -6.41 52.16 20.0 +1 300.0 -6.07 52.22 20.0 +1 300.0 -5.72 52.28 20.0 +1 300.0 -5.37 52.33 20.0 +1 300.0 -5.01 52.38 20.0 +1 300.0 -4.64 52.42 20.0 +1 300.0 -4.26 52.46 20.0 +1 300.0 -3.89 52.5 20.0 +1 300.0 -3.5 52.53 20.0 +1 300.0 -3.11 52.56 20.0 +1 300.0 -2.72 52.59 20.0 +1 300.0 -2.32 52.61 20.0 +1 300.0 -1.92 52.63 20.0 +1 300.0 -1.52 52.65 20.0 +1 300.0 -1.12 52.66 20.0 +1 300.0 -0.71 52.67 20.0 +1 300.0 -0.3 52.67 20.0 +1 300.0 0.1 52.67 20.0 +1 300.0 0.51 52.67 20.0 +1 300.0 0.92 52.66 20.0 +1 300.0 1.32 52.65 20.0 +1 300.0 1.72 52.64 20.0 +1 300.0 2.13 52.62 20.0 +1 300.0 2.52 52.6 20.0 +1 300.0 2.92 52.58 20.0 +1 300.0 3.31 52.55 20.0 +1 300.0 3.7 52.52 20.0 +1 300.0 4.08 52.48 20.0 +1 300.0 4.46 52.44 20.0 +1 300.0 4.83 52.4 20.0 +1 300.0 5.19 52.35 20.0 +1 300.0 5.55 52.3 20.0 +1 300.0 5.9 52.25 20.0 +1 300.0 6.24 52.19 20.0 +1 300.0 6.58 52.13 20.0 +1 300.0 6.9 52.07 20.0 +1 300.0 7.22 52.0 20.0 +1 300.0 7.53 51.93 20.0 +1 300.0 7.82 51.86 20.0 +1 300.0 8.39 51.7 20.0 +1 300.0 8.66 51.62 20.0 +1 300.0 8.91 51.53 20.0 +1 300.0 9.15 51.44 20.0 +1 300.0 9.39 51.35 20.0 +1 300.0 9.61 51.25 20.0 +1 300.0 9.81 51.15 20.0 +1 300.0 10.01 51.05 20.0 +1 300.0 10.19 50.94 20.0 +1 300.0 10.37 50.82 20.0 +1 300.0 10.54 50.7 20.0 +1 300.0 10.69 50.57 20.0 +1 300.0 10.83 50.44 20.0 +1 300.0 10.95 50.31 20.0 +1 300.0 11.05 50.17 20.0 +1 300.0 11.14 50.04 20.0 +1 300.0 11.21 49.89 20.0 +1 300.0 11.26 49.75 20.0 +1 300.0 11.3 49.6 20.0 +1 300.0 11.32 49.45 20.0 +1 300.0 11.33 49.29 20.0 +1 300.0 11.32 49.14 20.0 +1 300.0 11.29 48.98 20.0 +1 300.0 11.24 48.81 20.0 +1 300.0 11.19 48.65 20.0 +1 300.0 11.11 48.48 20.0 +1 300.0 11.02 48.31 20.0 +1 300.0 10.91 48.13 20.0 +1 300.0 10.79 47.96 20.0 +1 300.0 10.65 47.78 20.0 +1 300.0 10.5 47.6 20.0 +1 300.0 10.16 47.23 20.0 +1 300.0 9.97 47.04 20.0 +1 300.0 9.76 46.85 20.0 +1 300.0 9.31 46.47 20.0 +1 300.0 8.28 45.68 20.0 +1 300.0 8.0 45.48 20.0 +1 300.0 7.71 45.28 20.0 +1 300.0 7.1 44.87 20.0 +1 300.0 5.8 44.04 20.0 +1 300.0 3.01 42.35 20.0 +1 300.0 2.66 42.14 20.0 +1 300.0 2.31 41.92 20.0 +1 300.0 1.62 41.5 20.0 +1 300.0 0.27 40.65 20.0 +1 300.0 -0.05 40.44 20.0 +1 300.0 -0.36 40.23 20.0 +1 300.0 -0.97 39.82 20.0 +1 300.0 -2.09 38.99 20.0 +1 300.0 -2.32 38.8 20.0 +1 300.0 -2.55 38.62 20.0 +1 300.0 -2.97 38.24 20.0 +1 300.0 -3.17 38.06 20.0 +1 300.0 -3.35 37.88 20.0 +1 300.0 -3.69 37.52 20.0 +1 300.0 -3.84 37.34 20.0 +1 300.0 -3.97 37.16 20.0 +1 300.0 -4.1 36.99 20.0 +1 300.0 -4.21 36.81 20.0 +1 300.0 -4.3 36.64 20.0 +1 300.0 -4.39 36.47 20.0 +1 300.0 -4.46 36.3 20.0 +1 300.0 -4.51 36.14 20.0 +1 300.0 -4.56 35.98 20.0 +1 300.0 -4.59 35.81 20.0 +1 300.0 -4.6 35.66 20.0 +1 300.0 -4.6 35.5 20.0 +1 300.0 -4.59 35.34 20.0 +1 300.0 -4.56 35.19 20.0 +1 300.0 -4.52 35.04 20.0 +1 300.0 -4.46 34.89 20.0 +1 300.0 -4.39 34.75 20.0 +1 300.0 -4.31 34.6 20.0 +1 300.0 -4.21 34.47 20.0 +1 300.0 -4.09 34.33 20.0 +1 300.0 -3.97 34.19 20.0 +1 300.0 -3.83 34.06 20.0 +1 300.0 -3.67 33.93 20.0 +1 300.0 -3.5 33.8 20.0 +1 300.0 -3.32 33.68 20.0 +1 300.0 -3.13 33.56 20.0 +1 300.0 -2.92 33.44 20.0 +1 300.0 -2.7 33.32 20.0 +1 300.0 -2.47 33.21 20.0 +1 300.0 -2.22 33.1 20.0 +1 300.0 -1.7 32.89 20.0 +1 300.0 -1.42 32.79 20.0 +1 300.0 -1.13 32.7 20.0 +1 300.0 -0.83 32.6 20.0 +1 300.0 -0.52 32.51 20.0 +1 300.0 -0.2 32.42 20.0 +1 300.0 0.13 32.34 20.0 +1 300.0 0.81 32.18 20.0 +1 300.0 1.17 32.1 20.0 +1 300.0 1.53 32.03 20.0 +1 300.0 1.9 31.96 20.0 +1 300.0 2.27 31.9 20.0 +1 300.0 2.65 31.84 20.0 +1 300.0 3.04 31.78 20.0 +1 300.0 3.82 31.67 20.0 +1 300.0 4.22 31.63 20.0 +1 300.0 4.62 31.58 20.0 +1 300.0 5.03 31.54 20.0 +1 300.0 5.43 31.5 20.0 +1 300.0 5.84 31.47 20.0 +1 300.0 6.25 31.44 20.0 +1 300.0 6.66 31.41 20.0 +1 300.0 7.07 31.39 20.0 +1 300.0 7.51 31.36 20.0 +1 300.0 7.96 31.34 20.0 +1 300.0 8.4 31.33 20.0 +1 300.0 8.84 31.32 20.0 +1 300.0 9.27 31.32 20.0 +1 300.0 9.7 31.31 20.0 +1 300.0 10.12 31.32 20.0 +1 300.0 10.54 31.32 20.0 +1 300.0 10.96 31.33 20.0 +1 300.0 11.36 31.34 20.0 +1 300.0 11.76 31.36 20.0 +1 300.0 12.15 31.38 20.0 +1 300.0 12.53 31.41 20.0 +1 300.0 12.91 31.44 20.0 +1 300.0 13.27 31.47 20.0 +1 300.0 13.62 31.51 20.0 +1 300.0 13.96 31.55 20.0 +1 300.0 14.29 31.59 20.0 +1 300.0 14.61 31.64 20.0 +1 300.0 14.92 31.69 20.0 +1 300.0 15.21 31.75 20.0 +1 300.0 15.49 31.81 20.0 +1 300.0 15.75 31.87 20.0 +1 300.0 16.0 31.94 20.0 +1 300.0 16.24 32.01 20.0 +1 300.0 16.46 32.08 20.0 +1 300.0 16.67 32.16 20.0 +1 300.0 16.86 32.23 20.0 +1 300.0 17.03 32.32 20.0 +1 300.0 17.19 32.4 20.0 +1 300.0 17.33 32.49 20.0 +1 300.0 17.46 32.59 20.0 +1 300.0 17.57 32.68 20.0 +1 300.0 17.66 32.78 20.0 +1 300.0 17.74 32.88 20.0 +1 300.0 17.8 32.99 20.0 +1 300.0 17.84 33.09 20.0 +1 300.0 17.87 33.2 20.0 +1 300.0 17.88 33.32 20.0 +1 300.0 17.87 33.43 20.0 +1 300.0 17.84 33.55 20.0 +1 300.0 17.8 33.67 20.0 +1 300.0 17.74 33.79 20.0 +1 300.0 17.67 33.92 20.0 +1 300.0 17.58 34.05 20.0 +1 300.0 17.47 34.17 20.0 +1 300.0 17.35 34.31 20.0 +1 300.0 17.21 34.44 20.0 +1 300.0 17.06 34.58 20.0 +1 300.0 16.9 34.72 20.0 +1 300.0 16.52 35.0 20.0 +1 300.0 16.31 35.14 20.0 +1 300.0 16.09 35.28 20.0 +1 300.0 15.61 35.58 20.0 +1 300.0 15.36 35.73 20.0 +1 300.0 15.09 35.88 20.0 +1 300.0 14.52 36.18 20.0 +1 300.0 13.28 36.81 20.0 +1 300.0 12.96 36.96 20.0 +1 300.0 12.63 37.12 20.0 +1 300.0 11.95 37.43 20.0 +1 300.0 10.56 38.05 20.0 +1 300.0 7.75 39.3 20.0 +1 300.0 7.4 39.46 20.0 +1 300.0 7.07 39.61 20.0 +1 300.0 6.41 39.91 20.0 +1 300.0 5.17 40.51 20.0 +1 300.0 4.88 40.66 20.0 +1 300.0 4.6 40.8 20.0 +1 300.0 4.07 41.09 20.0 +1 300.0 3.83 41.23 20.0 +1 300.0 3.59 41.37 20.0 +1 300.0 3.15 41.64 20.0 +1 300.0 2.95 41.77 20.0 +1 300.0 2.76 41.9 20.0 +1 300.0 2.58 42.03 20.0 +1 300.0 2.42 42.16 20.0 +1 300.0 2.28 42.28 20.0 +1 300.0 2.15 42.4 20.0 +1 300.0 2.03 42.52 20.0 +1 300.0 1.93 42.64 20.0 +1 300.0 1.84 42.75 20.0 +1 300.0 1.77 42.87 20.0 +1 300.0 1.71 42.98 20.0 +1 300.0 1.67 43.08 20.0 +1 300.0 1.65 43.19 20.0 +1 300.0 1.64 43.29 20.0 +1 300.0 1.65 43.39 20.0 +1 300.0 1.67 43.48 20.0 +1 300.0 1.71 43.58 20.0 +1 300.0 1.76 43.66 20.0 +1 300.0 1.84 43.75 20.0 +1 300.0 1.93 43.84 20.0 +1 300.0 2.03 43.91 20.0 +1 300.0 2.15 43.99 20.0 +1 300.0 2.29 44.06 20.0 +1 300.0 2.44 44.13 20.0 +1 300.0 2.6 44.2 20.0 +1 300.0 2.79 44.27 20.0 +1 300.0 2.98 44.32 20.0 +1 300.0 3.19 44.38 20.0 +1 300.0 3.42 44.43 20.0 +1 300.0 3.66 44.48 20.0 +1 300.0 3.92 44.53 20.0 +1 300.0 4.18 44.57 20.0 +1 300.0 4.44 44.6 20.0 +1 300.0 4.71 44.63 20.0 +1 300.0 5.0 44.66 20.0 +1 300.0 5.29 44.69 20.0 +1 300.0 5.59 44.71 20.0 +1 300.0 5.9 44.73 20.0 +1 300.0 6.22 44.74 20.0 +1 300.0 6.55 44.75 20.0 +1 300.0 6.89 44.76 20.0 +1 300.0 7.24 44.77 20.0 +1 300.0 7.59 44.77 20.0 +1 300.0 7.95 44.76 20.0 +1 300.0 8.31 44.76 20.0 +1 300.0 8.68 44.75 20.0 +1 300.0 9.06 44.73 20.0 +1 300.0 9.44 44.72 20.0 +1 300.0 9.83 44.7 20.0 +1 300.0 10.22 44.67 20.0 +1 300.0 10.61 44.64 20.0 +1 300.0 11.0 44.61 20.0 +1 300.0 11.4 44.58 20.0 +1 300.0 11.8 44.54 20.0 +1 300.0 12.6 44.45 20.0 +1 300.0 13.0 44.4 20.0 +1 300.0 13.4 44.35 20.0 +1 300.0 13.8 44.29 20.0 +1 300.0 14.2 44.23 20.0 +1 300.0 14.6 44.16 20.0 +1 300.0 14.99 44.1 20.0 +1 300.0 15.77 43.95 20.0 +1 300.0 16.15 43.87 20.0 +1 300.0 16.52 43.79 20.0 +1 300.0 17.26 43.61 20.0 +1 300.0 17.62 43.52 20.0 +1 300.0 17.98 43.42 20.0 +1 300.0 18.66 43.22 20.0 +1 300.0 18.99 43.11 20.0 +1 300.0 19.31 43.0 20.0 +1 300.0 19.63 42.88 20.0 +1 300.0 19.93 42.77 20.0 +1 300.0 20.22 42.65 20.0 +1 300.0 20.51 42.52 20.0 +1 300.0 21.04 42.26 20.0 +1 300.0 21.29 42.13 20.0 +1 300.0 21.53 41.99 20.0 +1 300.0 21.76 41.85 20.0 +1 300.0 21.97 41.7 20.0 +1 300.0 22.18 41.56 20.0 +1 300.0 22.37 41.41 20.0 +1 300.0 22.54 41.25 20.0 +1 300.0 22.71 41.1 20.0 +1 300.0 22.86 40.94 20.0 +1 300.0 23.0 40.77 20.0 +1 300.0 23.12 40.61 20.0 +1 300.0 23.23 40.44 20.0 +1 300.0 23.32 40.27 20.0 +1 300.0 23.41 40.09 20.0 +1 300.0 23.48 39.92 20.0 +1 300.0 23.53 39.74 20.0 +1 300.0 23.57 39.54 20.0 +1 300.0 23.6 39.34 20.0 +1 300.0 23.61 39.14 20.0 +1 300.0 23.6 38.93 20.0 +1 300.0 23.57 38.72 20.0 +1 300.0 23.53 38.51 20.0 +1 300.0 23.48 38.29 20.0 +1 300.0 23.4 38.08 20.0 +1 300.0 23.32 37.85 20.0 +1 300.0 23.21 37.63 20.0 +1 300.0 23.09 37.41 20.0 +1 300.0 22.95 37.18 20.0 +1 300.0 22.8 36.95 20.0 +1 300.0 22.64 36.72 20.0 +1 300.0 22.27 36.24 20.0 +1 300.0 22.06 36.01 20.0 +1 300.0 21.84 35.77 20.0 +1 300.0 21.36 35.28 20.0 +1 300.0 20.27 34.28 20.0 +1 300.0 19.98 34.03 20.0 +1 300.0 19.67 33.78 20.0 +1 300.0 19.03 33.27 20.0 +1 300.0 17.67 32.23 20.0 +1 300.0 17.32 31.97 20.0 +1 300.0 16.96 31.71 20.0 +1 300.0 16.24 31.19 20.0 +1 300.0 14.77 30.14 20.0 +1 300.0 14.4 29.88 20.0 +1 300.0 14.04 29.61 20.0 +1 300.0 13.32 29.09 20.0 +1 300.0 11.92 28.04 20.0 +1 300.0 11.59 27.78 20.0 +1 300.0 11.26 27.52 20.0 +1 300.0 10.63 27.01 20.0 +1 300.0 10.32 26.75 20.0 +1 300.0 10.03 26.5 20.0 +1 300.0 9.47 25.99 20.0 +1 300.0 9.2 25.74 20.0 +1 300.0 8.95 25.49 20.0 +1 300.0 8.48 24.99 20.0 +1 300.0 8.27 24.75 20.0 +1 300.0 8.06 24.5 20.0 +1 300.0 7.7 24.02 20.0 +1 300.0 7.55 23.8 20.0 +1 300.0 7.41 23.58 20.0 +1 300.0 7.29 23.36 20.0 +1 300.0 7.18 23.14 20.0 +1 300.0 7.08 22.93 20.0 +1 300.0 7.0 22.71 20.0 +1 300.0 6.92 22.5 20.0 +1 300.0 6.87 22.3 20.0 +1 300.0 6.83 22.09 20.0 +1 300.0 6.8 21.88 20.0 +1 300.0 6.78 21.68 20.0 +1 300.0 6.79 21.48 20.0 +1 300.0 6.8 21.29 20.0 +1 300.0 6.83 21.09 20.0 +1 300.0 6.87 20.9 20.0 +1 300.0 6.93 20.71 20.0 +1 300.0 7.0 20.52 20.0 +1 300.0 7.09 20.34 20.0 +1 300.0 7.19 20.15 20.0 +1 300.0 7.31 19.98 20.0 +1 300.0 7.43 19.8 20.0 +1 300.0 7.58 19.63 20.0 +1 300.0 7.73 19.45 20.0 +1 300.0 7.9 19.29 20.0 +1 300.0 8.09 19.12 20.0 +1 300.0 8.28 18.96 20.0 +1 300.0 8.49 18.8 20.0 +1 300.0 8.71 18.64 20.0 +1 300.0 8.94 18.49 20.0 +1 300.0 9.19 18.34 20.0 +1 300.0 9.71 18.05 20.0 +1 300.0 9.99 17.91 20.0 +1 300.0 10.28 17.77 20.0 +1 300.0 10.88 17.51 20.0 +1 300.0 11.2 17.38 20.0 +1 300.0 11.53 17.26 20.0 +1 300.0 12.2 17.02 20.0 +1 300.0 12.55 16.91 20.0 +1 300.0 12.91 16.8 20.0 +1 300.0 13.64 16.58 20.0 +1 300.0 14.01 16.48 20.0 +1 300.0 14.39 16.39 20.0 +1 300.0 15.16 16.2 20.0 +1 300.0 15.55 16.12 20.0 +1 300.0 15.94 16.04 20.0 +1 300.0 16.73 15.88 20.0 +1 300.0 17.13 15.81 20.0 +1 300.0 17.52 15.74 20.0 +1 300.0 18.32 15.61 20.0 +1 300.0 18.71 15.55 20.0 +1 300.0 19.1 15.5 20.0 +1 300.0 19.49 15.45 20.0 +1 300.0 19.88 15.4 20.0 +1 300.0 20.26 15.36 20.0 +1 300.0 20.64 15.32 20.0 +1 300.0 21.02 15.28 20.0 +1 300.0 21.39 15.24 20.0 +1 300.0 21.74 15.21 20.0 +1 300.0 22.09 15.19 20.0 +1 300.0 22.44 15.17 20.0 +1 300.0 22.78 15.15 20.0 +1 300.0 23.11 15.13 20.0 +1 300.0 23.43 15.12 20.0 +1 300.0 23.75 15.11 20.0 +1 300.0 24.05 15.1 20.0 +1 300.0 24.35 15.1 20.0 +1 300.0 24.64 15.1 20.0 +1 300.0 24.91 15.1 20.0 +1 300.0 25.18 15.1 20.0 +1 300.0 25.44 15.11 20.0 +1 300.0 25.68 15.12 20.0 +1 300.0 25.92 15.14 20.0 +1 300.0 26.14 15.16 20.0 +1 300.0 26.35 15.18 20.0 +1 300.0 26.55 15.2 20.0 +1 300.0 26.74 15.23 20.0 +1 300.0 26.91 15.26 20.0 +1 300.0 27.08 15.29 20.0 +1 300.0 27.23 15.32 20.0 +1 300.0 27.36 15.36 20.0 +1 300.0 27.48 15.4 20.0 +1 300.0 27.59 15.45 20.0 +1 300.0 27.69 15.49 20.0 +1 300.0 27.77 15.54 20.0 +1 300.0 27.84 15.59 20.0 +1 300.0 27.89 15.64 20.0 +1 300.0 27.93 15.7 20.0 +1 300.0 27.95 15.76 20.0 +1 300.0 27.96 15.82 20.0 +1 300.0 27.96 15.88 20.0 +1 300.0 27.95 15.95 20.0 +1 300.0 27.92 16.01 20.0 +1 300.0 27.87 16.08 20.0 +1 300.0 27.82 16.16 20.0 +1 300.0 27.74 16.23 20.0 +1 300.0 27.66 16.3 20.0 +1 300.0 27.56 16.38 20.0 +1 300.0 27.45 16.46 20.0 +1 300.0 27.33 16.54 20.0 +1 300.0 27.04 16.71 20.0 +1 300.0 26.88 16.8 20.0 +1 300.0 26.71 16.89 20.0 +1 300.0 26.33 17.07 20.0 +1 300.0 26.12 17.16 20.0 +1 300.0 25.9 17.26 20.0 +1 300.0 25.43 17.45 20.0 +1 300.0 24.39 17.85 20.0 +1 300.0 21.93 18.69 20.0 +1 300.0 21.57 18.81 20.0 +1 300.0 21.21 18.93 20.0 +1 300.0 20.47 19.16 20.0 +1 300.0 18.98 19.64 20.0 +1 300.0 16.04 20.57 20.0 +1 300.0 15.69 20.69 20.0 +1 300.0 15.35 20.8 20.0 +1 300.0 14.68 21.02 20.0 +1 300.0 13.46 21.46 20.0 +1 300.0 13.18 21.56 20.0 +1 300.0 12.9 21.67 20.0 +1 300.0 12.4 21.87 20.0 +1 300.0 12.16 21.97 20.0 +1 300.0 11.94 22.06 20.0 +1 300.0 11.53 22.25 20.0 +1 300.0 11.35 22.34 20.0 +1 300.0 11.18 22.43 20.0 +1 300.0 11.02 22.52 20.0 +1 300.0 10.88 22.6 20.0 +1 300.0 10.76 22.68 20.0 +1 300.0 10.65 22.76 20.0 +1 300.0 10.55 22.84 20.0 +1 300.0 10.48 22.91 20.0 +1 300.0 10.41 22.98 20.0 +1 300.0 10.37 23.05 20.0 +1 300.0 10.34 23.12 20.0 +1 300.0 10.32 23.18 20.0 +1 300.0 10.32 23.24 20.0 +1 300.0 10.34 23.3 20.0 +1 300.0 10.38 23.35 20.0 +1 300.0 10.43 23.4 20.0 +1 300.0 10.5 23.45 20.0 +1 300.0 10.58 23.5 20.0 +1 300.0 10.68 23.54 20.0 +1 300.0 10.79 23.58 20.0 +1 300.0 10.93 23.61 20.0 +1 300.0 11.07 23.64 20.0 +1 300.0 11.23 23.67 20.0 +1 300.0 11.41 23.7 20.0 +1 300.0 11.6 23.72 20.0 +1 300.0 11.81 23.74 20.0 +1 300.0 12.03 23.75 20.0 +1 300.0 12.26 23.76 20.0 +1 300.0 12.51 23.77 20.0 +1 300.0 12.77 23.77 20.0 +1 300.0 13.04 23.77 20.0 +1 300.0 13.32 23.77 20.0 +1 300.0 13.6 23.76 20.0 +1 300.0 13.89 23.75 20.0 +1 300.0 14.18 23.73 20.0 +1 300.0 14.49 23.72 20.0 +1 300.0 14.8 23.7 20.0 +1 300.0 15.12 23.67 20.0 +1 300.0 15.45 23.64 20.0 +1 300.0 15.78 23.61 20.0 +1 300.0 16.13 23.58 20.0 +1 300.0 16.48 23.54 20.0 +1 300.0 16.83 23.5 20.0 +1 300.0 17.19 23.45 20.0 +1 300.0 17.55 23.4 20.0 +1 300.0 17.92 23.35 20.0 +1 300.0 18.67 23.23 20.0 +1 300.0 19.05 23.17 20.0 +1 300.0 19.42 23.1 20.0 +1 300.0 20.19 22.96 20.0 +1 300.0 20.57 22.88 20.0 +1 300.0 20.95 22.8 20.0 +1 300.0 21.71 22.62 20.0 +1 300.0 22.09 22.53 20.0 +1 300.0 22.47 22.43 20.0 +1 300.0 23.21 22.23 20.0 +1 300.0 23.57 22.13 20.0 +1 300.0 23.93 22.02 20.0 +1 300.0 24.64 21.78 20.0 +1 300.0 24.98 21.66 20.0 +1 300.0 25.32 21.54 20.0 +1 300.0 25.98 21.28 20.0 +1 300.0 26.29 21.14 20.0 +1 300.0 26.6 21.01 20.0 +1 300.0 27.19 20.72 20.0 +1 300.0 27.46 20.57 20.0 +1 300.0 27.73 20.42 20.0 +1 300.0 27.99 20.26 20.0 +1 300.0 28.24 20.11 20.0 +1 300.0 28.48 19.94 20.0 +1 300.0 28.7 19.78 20.0 +1 300.0 29.12 19.44 20.0 +1 300.0 29.3 19.27 20.0 +1 300.0 29.48 19.09 20.0 +1 300.0 29.64 18.91 20.0 +1 300.0 29.79 18.72 20.0 +1 300.0 29.93 18.54 20.0 +1 300.0 30.05 18.35 20.0 +1 300.0 30.16 18.16 20.0 +1 300.0 30.26 17.96 20.0 +1 300.0 30.34 17.76 20.0 +1 300.0 30.41 17.56 20.0 +1 300.0 30.46 17.36 20.0 +1 300.0 30.5 17.15 20.0 +1 300.0 30.52 16.94 20.0 +1 300.0 30.53 16.73 20.0 +1 300.0 30.53 16.52 20.0 +1 300.0 30.51 16.3 20.0 +1 300.0 30.47 16.07 20.0 +1 300.0 30.42 15.83 20.0 +1 300.0 30.35 15.58 20.0 +1 300.0 30.27 15.34 20.0 +1 300.0 30.16 15.09 20.0 +1 300.0 30.05 14.84 20.0 +1 300.0 29.91 14.59 20.0 +1 300.0 29.76 14.33 20.0 +1 300.0 29.6 14.07 20.0 +1 300.0 29.42 13.81 20.0 +1 300.0 29.01 13.29 20.0 +1 300.0 28.79 13.02 20.0 +1 300.0 28.55 12.75 20.0 +1 300.0 28.04 12.21 20.0 +1 300.0 27.77 11.94 20.0 +1 300.0 27.48 11.66 20.0 +1 300.0 26.88 11.11 20.0 +1 300.0 25.55 9.98 20.0 +1 300.0 22.57 7.69 20.0 +1 300.0 22.18 7.41 20.0 +1 300.0 21.79 7.12 20.0 +1 300.0 21.0 6.54 20.0 +1 300.0 19.44 5.38 20.0 +1 300.0 19.06 5.09 20.0 +1 300.0 18.68 4.81 20.0 +1 300.0 17.93 4.23 20.0 +1 300.0 16.51 3.1 20.0 +1 300.0 16.18 2.81 20.0 +1 300.0 15.85 2.53 20.0 +1 300.0 15.23 1.97 20.0 +1 300.0 14.93 1.7 20.0 +1 300.0 14.65 1.42 20.0 +1 300.0 14.12 0.88 20.0 +1 300.0 13.87 0.61 20.0 +1 300.0 13.63 0.34 20.0 +1 300.0 13.41 0.07 20.0 +1 300.0 13.2 -0.19 20.0 +1 300.0 13.01 -0.46 20.0 +1 300.0 12.83 -0.72 20.0 +1 300.0 12.67 -0.98 20.0 +1 300.0 12.52 -1.23 20.0 +1 300.0 12.39 -1.47 20.0 +1 300.0 12.28 -1.7 20.0 +1 300.0 12.19 -1.94 20.0 +1 300.0 12.11 -2.17 20.0 +1 300.0 12.04 -2.4 20.0 +1 300.0 11.98 -2.62 20.0 +1 300.0 11.94 -2.84 20.0 +1 300.0 11.92 -3.06 20.0 +1 300.0 11.9 -3.28 20.0 +1 300.0 11.91 -3.5 20.0 +1 300.0 11.93 -3.71 20.0 +1 300.0 11.96 -3.92 20.0 +1 300.0 12.01 -4.13 20.0 +1 300.0 12.07 -4.34 20.0 +1 300.0 12.14 -4.54 20.0 +1 300.0 12.23 -4.74 20.0 +1 300.0 12.34 -4.94 20.0 +1 300.0 12.45 -5.13 20.0 +1 300.0 12.58 -5.32 20.0 +1 300.0 12.73 -5.51 20.0 +1 300.0 12.89 -5.7 20.0 +1 300.0 13.06 -5.88 20.0 +1 300.0 13.24 -6.05 20.0 +1 300.0 13.44 -6.23 20.0 +1 300.0 13.65 -6.4 20.0 +1 300.0 13.87 -6.57 20.0 +1 300.0 14.1 -6.74 20.0 +1 300.0 14.35 -6.9 20.0 +1 300.0 14.6 -7.06 20.0 +1 300.0 14.86 -7.22 20.0 +1 300.0 15.42 -7.52 20.0 +1 300.0 15.72 -7.66 20.0 +1 300.0 16.02 -7.81 20.0 +1 300.0 16.65 -8.08 20.0 +1 300.0 16.98 -8.21 20.0 +1 300.0 17.31 -8.34 20.0 +1 300.0 17.99 -8.59 20.0 +1 300.0 18.34 -8.7 20.0 +1 300.0 18.7 -8.82 20.0 +1 300.0 19.42 -9.04 20.0 +1 300.0 20.9 -9.43 20.0 +1 300.0 21.28 -9.52 20.0 +1 300.0 21.65 -9.6 20.0 +1 300.0 22.4 -9.76 20.0 +1 300.0 22.77 -9.83 20.0 +1 300.0 23.14 -9.9 20.0 +1 300.0 23.87 -10.03 20.0 +1 300.0 24.23 -10.09 20.0 +1 300.0 24.58 -10.15 20.0 +1 300.0 24.94 -10.2 20.0 +1 300.0 25.28 -10.25 20.0 +1 300.0 25.62 -10.29 20.0 +1 300.0 25.96 -10.34 20.0 +1 300.0 26.6 -10.41 20.0 +1 300.0 26.91 -10.44 20.0 +1 300.0 27.21 -10.46 20.0 +1 300.0 27.5 -10.49 20.0 +1 300.0 27.78 -10.51 20.0 +1 300.0 28.05 -10.53 20.0 +1 300.0 28.31 -10.54 20.0 +1 300.0 28.57 -10.55 20.0 +1 300.0 28.81 -10.56 20.0 +1 300.0 29.04 -10.56 20.0 +1 300.0 29.26 -10.56 20.0 +1 300.0 29.47 -10.56 20.0 +1 300.0 29.66 -10.56 20.0 +1 300.0 29.85 -10.55 20.0 +1 300.0 30.02 -10.54 20.0 +1 300.0 30.18 -10.52 20.0 +1 300.0 30.33 -10.5 20.0 +1 300.0 30.46 -10.48 20.0 +1 300.0 30.58 -10.46 20.0 +1 300.0 30.69 -10.44 20.0 +1 300.0 30.78 -10.41 20.0 +1 300.0 30.86 -10.37 20.0 +1 300.0 30.93 -10.34 20.0 +1 300.0 30.98 -10.3 20.0 +1 300.0 31.02 -10.26 20.0 +1 300.0 31.05 -10.22 20.0 +1 300.0 31.06 -10.18 20.0 +1 300.0 31.05 -10.13 20.0 +1 300.0 31.04 -10.08 20.0 +1 300.0 31.01 -10.03 20.0 +1 300.0 30.96 -9.97 20.0 +1 300.0 30.9 -9.91 20.0 +1 300.0 30.83 -9.86 20.0 +1 300.0 30.74 -9.79 20.0 +1 300.0 30.64 -9.73 20.0 +1 300.0 30.52 -9.66 20.0 +1 300.0 30.4 -9.6 20.0 +1 300.0 30.25 -9.53 20.0 +1 300.0 30.1 -9.46 20.0 +1 300.0 29.75 -9.31 20.0 +1 300.0 29.56 -9.23 20.0 +1 300.0 29.36 -9.15 20.0 +1 300.0 28.92 -8.99 20.0 +1 300.0 27.9 -8.64 20.0 +1 300.0 27.62 -8.55 20.0 +1 300.0 27.34 -8.46 20.0 +1 300.0 26.73 -8.28 20.0 +1 300.0 25.44 -7.9 20.0 +1 300.0 22.59 -7.1 20.0 +1 300.0 22.19 -6.99 20.0 +1 300.0 21.79 -6.88 20.0 +1 300.0 20.98 -6.66 20.0 +1 300.0 19.37 -6.22 20.0 +1 300.0 18.98 -6.11 20.0 +1 300.0 18.58 -6.0 20.0 +1 300.0 17.81 -5.78 20.0 +1 300.0 16.35 -5.36 20.0 +1 300.0 16.0 -5.26 20.0 +1 300.0 15.66 -5.16 20.0 +1 300.0 15.02 -4.95 20.0 +1 300.0 14.71 -4.86 20.0 +1 300.0 14.41 -4.76 20.0 +1 300.0 13.85 -4.57 20.0 +1 300.0 13.59 -4.48 20.0 +1 300.0 13.34 -4.39 20.0 +1 300.0 12.88 -4.21 20.0 +1 300.0 12.68 -4.13 20.0 +1 300.0 12.48 -4.04 20.0 +1 300.0 12.14 -3.89 20.0 +1 300.0 11.99 -3.81 20.0 +1 300.0 11.86 -3.74 20.0 +1 300.0 11.74 -3.67 20.0 +1 300.0 11.64 -3.6 20.0 +1 300.0 11.55 -3.53 20.0 +1 300.0 11.49 -3.47 20.0 +1 300.0 11.43 -3.41 20.0 +1 300.0 11.4 -3.35 20.0 +1 300.0 11.38 -3.29 20.0 +1 300.0 11.37 -3.24 20.0 +1 300.0 11.39 -3.19 20.0 +1 300.0 11.41 -3.15 20.0 +1 300.0 11.46 -3.1 20.0 +1 300.0 11.52 -3.06 20.0 +1 300.0 11.6 -3.02 20.0 +1 300.0 11.69 -2.99 20.0 +1 300.0 11.8 -2.96 20.0 +1 300.0 11.93 -2.93 20.0 +1 300.0 12.07 -2.91 20.0 +1 300.0 12.22 -2.89 20.0 +1 300.0 12.39 -2.87 20.0 +1 300.0 12.58 -2.86 20.0 +1 300.0 12.78 -2.85 20.0 +1 300.0 12.99 -2.84 20.0 +1 300.0 13.21 -2.84 20.0 +1 300.0 13.45 -2.84 20.0 +1 300.0 13.7 -2.84 20.0 +1 300.0 13.97 -2.85 20.0 +1 300.0 14.24 -2.86 20.0 +1 300.0 14.53 -2.88 20.0 +1 300.0 14.82 -2.9 20.0 +1 300.0 15.13 -2.92 20.0 +1 300.0 15.43 -2.95 20.0 +1 300.0 15.73 -2.98 20.0 +1 300.0 16.04 -3.01 20.0 +1 300.0 16.35 -3.04 20.0 +1 300.0 16.68 -3.08 20.0 +1 300.0 17.0 -3.12 20.0 +1 300.0 17.68 -3.22 20.0 +1 300.0 18.02 -3.27 20.0 +1 300.0 18.37 -3.33 20.0 +1 300.0 19.07 -3.45 20.0 +1 300.0 19.43 -3.52 20.0 +1 300.0 19.78 -3.59 20.0 +1 300.0 20.5 -3.74 20.0 +1 300.0 20.86 -3.82 20.0 +1 300.0 21.21 -3.91 20.0 +1 300.0 21.93 -4.09 20.0 +1 300.0 22.28 -4.18 20.0 +1 300.0 22.63 -4.28 20.0 +1 300.0 23.31 -4.49 20.0 +1 300.0 23.65 -4.6 20.0 +1 300.0 23.98 -4.71 20.0 +1 300.0 24.63 -4.95 20.0 +1 300.0 24.95 -5.07 20.0 +1 300.0 25.25 -5.2 20.0 +1 300.0 25.85 -5.46 20.0 +1 300.0 26.13 -5.6 20.0 +1 300.0 26.4 -5.74 20.0 +1 300.0 26.67 -5.88 20.0 +1 300.0 26.93 -6.03 20.0 +1 300.0 27.17 -6.18 20.0 +1 300.0 27.41 -6.33 20.0 +1 300.0 27.84 -6.65 20.0 +1 300.0 28.04 -6.81 20.0 +1 300.0 28.23 -6.98 20.0 +1 300.0 28.41 -7.15 20.0 +1 300.0 28.57 -7.32 20.0 +1 300.0 28.73 -7.5 20.0 +1 300.0 28.86 -7.67 20.0 +1 300.0 28.99 -7.86 20.0 +1 300.0 29.1 -8.04 20.0 +1 300.0 29.2 -8.23 20.0 +1 300.0 29.28 -8.42 20.0 +1 300.0 29.35 -8.61 20.0 +1 300.0 29.41 -8.8 20.0 +1 300.0 29.45 -9.0 20.0 +1 300.0 29.48 -9.2 20.0 +1 300.0 29.49 -9.41 20.0 +1 300.0 29.49 -9.61 20.0 +1 300.0 29.48 -9.82 20.0 +1 300.0 29.45 -10.03 20.0 +1 300.0 29.4 -10.24 20.0 +1 300.0 29.34 -10.46 20.0 +1 300.0 29.27 -10.68 20.0 +1 300.0 29.18 -10.9 20.0 +1 300.0 29.08 -11.12 20.0 +1 300.0 28.96 -11.34 20.0 +1 300.0 28.82 -11.59 20.0 +1 300.0 28.67 -11.84 20.0 +1 300.0 28.49 -12.09 20.0 +1 300.0 28.3 -12.34 20.0 +1 300.0 28.1 -12.59 20.0 +1 300.0 27.88 -12.85 20.0 +1 300.0 27.4 -13.37 20.0 +1 300.0 27.14 -13.63 20.0 +1 300.0 26.87 -13.9 20.0 +1 300.0 26.28 -14.43 20.0 +1 300.0 24.97 -15.51 20.0 +1 300.0 24.61 -15.79 20.0 +1 300.0 24.25 -16.06 20.0 +1 300.0 23.5 -16.62 20.0 +1 300.0 21.92 -17.73 20.0 +1 300.0 18.57 -19.98 20.0 +1 300.0 18.15 -20.27 20.0 +1 300.0 17.73 -20.55 20.0 +1 300.0 16.9 -21.11 20.0 +1 300.0 15.29 -22.22 20.0 +1 300.0 14.9 -22.5 20.0 +1 300.0 14.52 -22.77 20.0 +1 300.0 13.78 -23.32 20.0 +1 300.0 12.41 -24.4 20.0 +1 300.0 12.1 -24.67 20.0 +1 300.0 11.79 -24.93 20.0 +1 300.0 11.22 -25.45 20.0 +1 300.0 10.96 -25.71 20.0 +1 300.0 10.71 -25.97 20.0 +1 300.0 10.25 -26.48 20.0 +1 300.0 10.04 -26.73 20.0 +1 300.0 9.85 -26.97 20.0 +1 300.0 9.67 -27.22 20.0 +1 300.0 9.51 -27.46 20.0 +1 300.0 9.36 -27.7 20.0 +1 300.0 9.23 -27.94 20.0 +1 300.0 9.12 -28.18 20.0 +1 300.0 9.02 -28.41 20.0 +1 300.0 8.94 -28.63 20.0 +1 300.0 8.87 -28.86 20.0 +1 300.0 8.83 -29.07 20.0 +1 300.0 8.8 -29.29 20.0 +1 300.0 8.78 -29.5 20.0 +1 300.0 8.78 -29.72 20.0 +1 300.0 8.8 -29.92 20.0 +1 300.0 8.83 -30.13 20.0 +1 300.0 8.88 -30.33 20.0 +1 300.0 8.95 -30.53 20.0 +1 300.0 9.03 -30.72 20.0 +1 300.0 9.13 -30.91 20.0 +1 300.0 9.24 -31.1 20.0 +1 300.0 9.36 -31.29 20.0 +1 300.0 9.51 -31.47 20.0 +1 300.0 9.66 -31.64 20.0 +1 300.0 9.83 -31.82 20.0 +1 300.0 10.02 -31.99 20.0 +1 300.0 10.21 -32.16 20.0 +1 300.0 10.43 -32.32 20.0 +1 300.0 10.65 -32.48 20.0 +1 300.0 10.88 -32.63 20.0 +1 300.0 11.39 -32.93 20.0 +1 300.0 11.66 -33.08 20.0 +1 300.0 11.95 -33.22 20.0 +1 300.0 12.54 -33.48 20.0 +1 300.0 12.85 -33.61 20.0 +1 300.0 13.16 -33.73 20.0 +1 300.0 13.82 -33.97 20.0 +1 300.0 14.16 -34.08 20.0 +1 300.0 14.5 -34.19 20.0 +1 300.0 15.21 -34.39 20.0 +1 300.0 15.57 -34.49 20.0 +1 300.0 15.93 -34.58 20.0 +1 300.0 16.66 -34.75 20.0 +1 300.0 17.03 -34.83 20.0 +1 300.0 17.4 -34.9 20.0 +1 300.0 17.77 -34.97 20.0 +1 300.0 18.13 -35.04 20.0 +1 300.0 18.5 -35.1 20.0 +1 300.0 18.86 -35.16 20.0 +1 300.0 19.59 -35.26 20.0 +1 300.0 19.94 -35.3 20.0 +1 300.0 20.29 -35.35 20.0 +1 300.0 20.63 -35.38 20.0 +1 300.0 20.97 -35.42 20.0 +1 300.0 21.3 -35.44 20.0 +1 300.0 21.63 -35.47 20.0 +1 300.0 21.95 -35.49 20.0 +1 300.0 22.26 -35.51 20.0 +1 300.0 22.56 -35.52 20.0 +1 300.0 22.85 -35.53 20.0 +1 300.0 23.13 -35.53 20.0 +1 300.0 23.4 -35.53 20.0 +1 300.0 23.66 -35.53 20.0 +1 300.0 23.9 -35.52 20.0 +1 300.0 24.14 -35.51 20.0 +1 300.0 24.36 -35.5 20.0 +1 300.0 24.56 -35.48 20.0 +1 300.0 24.75 -35.46 20.0 +1 300.0 24.92 -35.44 20.0 +1 300.0 25.08 -35.41 20.0 +1 300.0 25.23 -35.38 20.0 +1 300.0 25.36 -35.34 20.0 +1 300.0 25.49 -35.31 20.0 +1 300.0 25.6 -35.27 20.0 +1 300.0 25.69 -35.23 20.0 +1 300.0 25.78 -35.18 20.0 +1 300.0 25.85 -35.13 20.0 +1 300.0 25.9 -35.08 20.0 +1 300.0 25.94 -35.03 20.0 +1 300.0 25.97 -34.97 20.0 +1 300.0 25.99 -34.91 20.0 +1 300.0 25.99 -34.85 20.0 +1 300.0 25.97 -34.78 20.0 +1 300.0 25.94 -34.72 20.0 +1 300.0 25.9 -34.65 20.0 +1 300.0 25.84 -34.58 20.0 +1 300.0 25.77 -34.5 20.0 +1 300.0 25.68 -34.42 20.0 +1 300.0 25.59 -34.34 20.0 +1 300.0 25.47 -34.26 20.0 +1 300.0 25.34 -34.17 20.0 +1 300.0 25.2 -34.09 20.0 +1 300.0 24.88 -33.91 20.0 +1 300.0 24.7 -33.81 20.0 +1 300.0 24.51 -33.72 20.0 +1 300.0 24.09 -33.52 20.0 +1 300.0 23.86 -33.42 20.0 +1 300.0 23.61 -33.32 20.0 +1 300.0 23.09 -33.11 20.0 +1 300.0 21.93 -32.67 20.0 +1 300.0 19.2 -31.74 20.0 +1 300.0 12.95 -29.75 20.0 +1 300.0 12.53 -29.61 20.0 +1 300.0 12.12 -29.48 20.0 +1 300.0 11.3 -29.21 20.0 +1 300.0 9.75 -28.68 20.0 +1 300.0 9.38 -28.55 20.0 +1 300.0 9.03 -28.42 20.0 +1 300.0 8.34 -28.17 20.0 +1 300.0 7.1 -27.68 20.0 +1 300.0 6.83 -27.56 20.0 +1 300.0 6.56 -27.44 20.0 +1 300.0 6.07 -27.21 20.0 +1 300.0 5.85 -27.1 20.0 +1 300.0 5.64 -26.99 20.0 +1 300.0 5.27 -26.78 20.0 +1 300.0 5.11 -26.68 20.0 +1 300.0 4.96 -26.58 20.0 +1 300.0 4.83 -26.48 20.0 +1 300.0 4.72 -26.38 20.0 +1 300.0 4.62 -26.29 20.0 +1 300.0 4.54 -26.2 20.0 +1 300.0 4.48 -26.11 20.0 +1 300.0 4.43 -26.03 20.0 +1 300.0 4.4 -25.94 20.0 +1 300.0 4.38 -25.86 20.0 +1 300.0 4.38 -25.79 20.0 +1 300.0 4.4 -25.71 20.0 +1 300.0 4.44 -25.64 20.0 +1 300.0 4.49 -25.58 20.0 +1 300.0 4.56 -25.51 20.0 +1 300.0 4.64 -25.45 20.0 +1 300.0 4.74 -25.39 20.0 +1 300.0 4.85 -25.34 20.0 +1 300.0 4.98 -25.29 20.0 +1 300.0 5.13 -25.24 20.0 +1 300.0 5.29 -25.19 20.0 +1 300.0 5.46 -25.15 20.0 +1 300.0 5.65 -25.11 20.0 +1 300.0 5.85 -25.08 20.0 +1 300.0 6.06 -25.05 20.0 +1 300.0 6.29 -25.02 20.0 +1 300.0 6.53 -25.0 20.0 +1 300.0 6.78 -24.98 20.0 +1 300.0 7.04 -24.96 20.0 +1 300.0 7.31 -24.95 20.0 +1 300.0 7.6 -24.94 20.0 +1 300.0 7.89 -24.94 20.0 +1 300.0 8.19 -24.94 20.0 +1 300.0 8.5 -24.94 20.0 +1 300.0 8.82 -24.95 20.0 +1 300.0 9.15 -24.96 20.0 +1 300.0 9.48 -24.98 20.0 +1 300.0 9.82 -24.99 20.0 +1 300.0 10.16 -25.02 20.0 +1 300.0 10.51 -25.04 20.0 +1 300.0 10.84 -25.07 20.0 +1 300.0 11.17 -25.1 20.0 +1 300.0 11.51 -25.14 20.0 +1 300.0 11.84 -25.18 20.0 +1 300.0 12.18 -25.22 20.0 +1 300.0 12.52 -25.27 20.0 +1 300.0 13.2 -25.37 20.0 +1 300.0 13.53 -25.43 20.0 +1 300.0 13.87 -25.49 20.0 +1 300.0 14.2 -25.55 20.0 +1 300.0 14.54 -25.62 20.0 +1 300.0 14.86 -25.69 20.0 +1 300.0 15.19 -25.76 20.0 +1 300.0 15.82 -25.92 20.0 +1 300.0 16.13 -26.0 20.0 +1 300.0 16.44 -26.09 20.0 +1 300.0 16.74 -26.18 20.0 +1 300.0 17.03 -26.28 20.0 +1 300.0 17.31 -26.38 20.0 +1 300.0 17.59 -26.48 20.0 +1 300.0 18.11 -26.69 20.0 +1 300.0 18.36 -26.8 20.0 +1 300.0 18.6 -26.91 20.0 +1 300.0 18.83 -27.03 20.0 +1 300.0 19.05 -27.15 20.0 +1 300.0 19.26 -27.28 20.0 +1 300.0 19.46 -27.4 20.0 +1 300.0 19.64 -27.53 20.0 +1 300.0 19.82 -27.67 20.0 +1 300.0 19.98 -27.8 20.0 +1 300.0 20.13 -27.94 20.0 +1 300.0 20.26 -28.09 20.0 +1 300.0 20.39 -28.23 20.0 +1 300.0 20.5 -28.38 20.0 +1 300.0 20.59 -28.53 20.0 +1 300.0 20.68 -28.68 20.0 +1 300.0 20.75 -28.84 20.0 +1 300.0 20.8 -29.0 20.0 +1 300.0 20.84 -29.16 20.0 +1 300.0 20.87 -29.33 20.0 +1 300.0 20.88 -29.49 20.0 +1 300.0 20.88 -29.66 20.0 +1 300.0 20.86 -29.84 20.0 +1 300.0 20.83 -30.01 20.0 +1 300.0 20.78 -30.19 20.0 +1 300.0 20.72 -30.37 20.0 +1 300.0 20.64 -30.55 20.0 +1 300.0 20.56 -30.73 20.0 +1 300.0 20.45 -30.92 20.0 +1 300.0 20.33 -31.11 20.0 +1 300.0 20.2 -31.3 20.0 +1 300.0 20.05 -31.49 20.0 +1 300.0 19.89 -31.69 20.0 +1 300.0 19.72 -31.89 20.0 +1 300.0 19.53 -32.08 20.0 +1 300.0 19.12 -32.49 20.0 +1 300.0 18.89 -32.69 20.0 +1 300.0 18.66 -32.9 20.0 +1 300.0 18.14 -33.31 20.0 +1 300.0 17.87 -33.52 20.0 +1 300.0 17.59 -33.72 20.0 +1 300.0 17.01 -34.14 20.0 +1 300.0 15.72 -34.99 20.0 +1 300.0 12.8 -36.73 20.0 +1 300.0 12.41 -36.95 20.0 +1 300.0 12.02 -37.17 20.0 +1 300.0 11.22 -37.61 20.0 +1 300.0 9.62 -38.49 20.0 +1 300.0 6.46 -40.24 20.0 +1 300.0 6.07 -40.45 20.0 +1 300.0 5.7 -40.67 20.0 +1 300.0 4.96 -41.1 20.0 +1 300.0 3.57 -41.94 20.0 +1 300.0 3.24 -42.15 20.0 +1 300.0 2.92 -42.36 20.0 +1 300.0 2.3 -42.77 20.0 +1 300.0 1.19 -43.57 20.0 +1 300.0 0.94 -43.76 20.0 +1 300.0 0.71 -43.96 20.0 +1 300.0 0.26 -44.34 20.0 +1 300.0 0.06 -44.53 20.0 +1 300.0 -0.13 -44.72 20.0 +1 300.0 -0.47 -45.09 20.0 +1 300.0 -0.62 -45.27 20.0 +1 300.0 -0.76 -45.44 20.0 +1 300.0 -0.88 -45.62 20.0 +1 300.0 -0.99 -45.79 20.0 +1 300.0 -1.09 -45.97 20.0 +1 300.0 -1.17 -46.13 20.0 +1 300.0 -1.24 -46.3 20.0 +1 300.0 -1.3 -46.47 20.0 +1 300.0 -1.34 -46.64 20.0 +1 300.0 -1.37 -46.81 20.0 +1 300.0 -1.38 -46.98 20.0 +1 300.0 -1.38 -47.15 20.0 +1 300.0 -1.36 -47.31 20.0 +1 300.0 -1.32 -47.47 20.0 +1 300.0 -1.27 -47.62 20.0 +1 300.0 -1.2 -47.77 20.0 +1 300.0 -1.11 -47.92 20.0 +1 300.0 -1.01 -48.07 20.0 +1 300.0 -0.9 -48.21 20.0 +1 300.0 -0.77 -48.35 20.0 +1 300.0 -0.63 -48.48 20.0 +1 300.0 -0.47 -48.61 20.0 +1 300.0 -0.29 -48.74 20.0 +1 300.0 -0.11 -48.86 20.0 +1 300.0 0.09 -48.98 20.0 +1 300.0 0.3 -49.1 20.0 +1 300.0 0.53 -49.21 20.0 +1 300.0 0.77 -49.31 20.0 +1 300.0 1.01 -49.42 20.0 +1 300.0 1.27 -49.52 20.0 +1 300.0 1.82 -49.7 20.0 +1 300.0 2.11 -49.79 20.0 +1 300.0 2.41 -49.88 20.0 +1 300.0 2.71 -49.95 20.0 +1 300.0 3.03 -50.03 20.0 +1 300.0 3.35 -50.1 20.0 +1 300.0 3.67 -50.17 20.0 +1 300.0 4.0 -50.23 20.0 +1 300.0 4.34 -50.29 20.0 +1 300.0 4.68 -50.34 20.0 +1 300.0 5.03 -50.39 20.0 +1 300.0 5.38 -50.44 20.0 +1 300.0 5.73 -50.48 20.0 +1 300.0 6.08 -50.51 20.0 +1 300.0 6.43 -50.55 20.0 +1 300.0 6.79 -50.57 20.0 +1 300.0 7.14 -50.6 20.0 +1 300.0 7.49 -50.62 20.0 +1 300.0 7.84 -50.63 20.0 +1 300.0 8.19 -50.64 20.0 +1 300.0 8.54 -50.65 20.0 +1 300.0 8.88 -50.65 20.0 +1 300.0 9.22 -50.65 20.0 +1 300.0 9.55 -50.65 20.0 +1 300.0 9.88 -50.63 20.0 +1 300.0 10.2 -50.62 20.0 +1 300.0 10.52 -50.6 20.0 +1 300.0 10.82 -50.58 20.0 +1 300.0 11.12 -50.55 20.0 +1 300.0 11.41 -50.52 20.0 +1 300.0 11.69 -50.49 20.0 +1 300.0 11.96 -50.45 20.0 +1 300.0 12.22 -50.4 20.0 +1 300.0 12.47 -50.36 20.0 +1 300.0 12.71 -50.3 20.0 +1 300.0 12.94 -50.25 20.0 +1 300.0 13.15 -50.19 20.0 +1 300.0 13.36 -50.13 20.0 +1 300.0 13.55 -50.06 20.0 +1 300.0 13.72 -49.99 20.0 +1 300.0 13.88 -49.92 20.0 +1 300.0 14.02 -49.84 20.0 +1 300.0 14.14 -49.77 20.0 +1 300.0 14.26 -49.69 20.0 +1 300.0 14.36 -49.6 20.0 +1 300.0 14.44 -49.52 20.0 +1 300.0 14.51 -49.43 20.0 +1 300.0 14.57 -49.34 20.0 +1 300.0 14.62 -49.24 20.0 +1 300.0 14.64 -49.15 20.0 +1 300.0 14.66 -49.05 20.0 +1 300.0 14.66 -48.94 20.0 +1 300.0 14.65 -48.84 20.0 +1 300.0 14.63 -48.73 20.0 +1 300.0 14.58 -48.62 20.0 +1 300.0 14.53 -48.5 20.0 +1 300.0 14.46 -48.39 20.0 +1 300.0 14.38 -48.27 20.0 +1 300.0 14.28 -48.15 20.0 +1 300.0 14.17 -48.02 20.0 +1 300.0 14.04 -47.9 20.0 +1 300.0 13.9 -47.77 20.0 +1 300.0 13.75 -47.63 20.0 +1 300.0 13.4 -47.37 20.0 +1 300.0 13.21 -47.23 20.0 +1 300.0 13.0 -47.09 20.0 +1 300.0 12.55 -46.8 20.0 +1 300.0 11.5 -46.21 20.0 +1 300.0 11.21 -46.06 20.0 +1 300.0 10.92 -45.9 20.0 +1 300.0 10.29 -45.59 20.0 +1 300.0 8.92 -44.95 20.0 +1 300.0 5.87 -43.61 20.0 +1 300.0 5.47 -43.44 20.0 +1 300.0 5.06 -43.27 20.0 +1 300.0 4.25 -42.93 20.0 +1 300.0 2.6 -42.24 20.0 +1 300.0 -0.6 -40.87 20.0 +1 300.0 -1.02 -40.68 20.0 +1 300.0 -1.44 -40.49 20.0 +1 300.0 -2.26 -40.12 20.0 +1 300.0 -3.76 -39.38 20.0 +1 300.0 -4.11 -39.2 20.0 +1 300.0 -4.45 -39.02 20.0 +1 300.0 -5.08 -38.66 20.0 +1 300.0 -5.38 -38.49 20.0 +1 300.0 -5.66 -38.32 20.0 +1 300.0 -6.18 -37.98 20.0 +1 300.0 -6.42 -37.81 20.0 +1 300.0 -6.64 -37.65 20.0 +1 300.0 -6.84 -37.48 20.0 +1 300.0 -7.03 -37.32 20.0 +1 300.0 -7.2 -37.16 20.0 +1 300.0 -7.35 -37.01 20.0 +1 300.0 -7.49 -36.86 20.0 +1 300.0 -7.61 -36.71 20.0 +1 300.0 -7.71 -36.56 20.0 +1 300.0 -7.79 -36.41 20.0 +1 300.0 -7.86 -36.27 20.0 +1 300.0 -7.9 -36.13 20.0 +1 300.0 -7.93 -36.0 20.0 +1 300.0 -7.95 -35.86 20.0 +1 300.0 -7.94 -35.73 20.0 +1 300.0 -7.92 -35.61 20.0 +1 300.0 -7.88 -35.48 20.0 +1 300.0 -7.82 -35.36 20.0 +1 300.0 -7.74 -35.25 20.0 +1 300.0 -7.65 -35.13 20.0 +1 300.0 -7.54 -35.02 20.0 +1 300.0 -7.42 -34.92 20.0 +1 300.0 -7.28 -34.81 20.0 +1 300.0 -7.12 -34.72 20.0 +1 300.0 -6.95 -34.62 20.0 +1 300.0 -6.76 -34.53 20.0 +1 300.0 -6.56 -34.44 20.0 +1 300.0 -6.34 -34.36 20.0 +1 300.0 -6.12 -34.28 20.0 +1 300.0 -5.87 -34.2 20.0 +1 300.0 -5.62 -34.13 20.0 +1 300.0 -5.35 -34.06 20.0 +1 300.0 -5.08 -33.99 20.0 +1 300.0 -4.79 -33.93 20.0 +1 300.0 -4.49 -33.88 20.0 +1 300.0 -4.18 -33.82 20.0 +1 300.0 -3.86 -33.77 20.0 +1 300.0 -3.54 -33.73 20.0 +1 300.0 -3.21 -33.69 20.0 +1 300.0 -2.87 -33.66 20.0 +1 300.0 -2.52 -33.62 20.0 +1 300.0 -2.17 -33.6 20.0 +1 300.0 -1.82 -33.57 20.0 +1 300.0 -1.46 -33.55 20.0 +1 300.0 -1.1 -33.54 20.0 +1 300.0 -0.73 -33.53 20.0 +1 300.0 -0.37 -33.52 20.0 +1 300.0 0.0 -33.52 20.0 +0 300.0 0.0 -33.52 6.0 +0 300.0 37.56 12.33 6.0 +1 300.0 37.56 12.33 -1.0 +1 300.0 37.56 0.88 -1.0 +1 300.0 49.01 0.88 -1.0 +1 300.0 49.01 12.33 -1.0 +1 300.0 37.56 12.33 -1.0 +0 300.0 37.56 12.33 6.0 +0 300.0 37.56 0.88 6.0 +1 300.0 37.56 0.88 -1.0 +1 300.0 37.56 -10.57 -1.0 +1 300.0 49.01 -10.57 -1.0 +1 300.0 49.01 0.88 -1.0 +1 300.0 37.56 0.88 -1.0 +0 300.0 37.56 0.88 6.0 +0 300.0 49.01 12.33 6.0 +1 300.0 49.01 12.33 -1.0 +1 300.0 52.08 15.01 -1.0 +0 300.0 52.08 15.01 6.0 +0 300.0 49.01 0.88 6.0 +1 300.0 49.01 0.88 -1.0 +1 300.0 52.08 6.21 -1.0 +1 300.0 52.08 15.01 -1.0 +1 300.0 43.29 15.01 -1.0 +1 300.0 37.56 12.33 -1.0 +0 300.0 37.56 12.33 6.0 +0 300.0 49.01 -10.57 6.0 +1 300.0 49.01 -10.57 -1.0 +1 300.0 52.08 -2.58 -1.0 +1 300.0 52.08 6.21 -1.0 +1 300.0 49.01 0.88 -1.0 +0 300.0 49.01 0.88 6.0 +0 300.0 49.01 0.88 20.0 +0 300.0 0.0 0.0 20.0 +0 300.0 0.0 0.0 20.0 diff --git a/doc/test/matlab_convert.py b/doc/test/matlab_convert.py new file mode 100755 index 0000000..cf750ac --- /dev/null +++ b/doc/test/matlab_convert.py @@ -0,0 +1,270 @@ +#!/usr/bin/env python +"""\ +The MIT License (MIT) + +Copyright (c) 2014 Sungeun K. Jeon + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +---------------------------------------------------------------------------------------- +""" +"""\ +G-code preprocessor for the grbl_sim.m MATLAB script. Parses the g-code program to a +specific file format for the MATLAB script to use. Based on PreGrbl by @chamnit. + +How to use: When running this python script, it will process the g-code program under +the filename "test.gcode" (may be changed below) and produces a file called "matlab.gcode" +that the grbl_sim.m MATLAB script will search for and execute. +""" + +import re +from math import * +from copy import * + +# -= SETTINGS =- +filein = 'test.gcode' # Input file name +fileout = 'matlab.gcode' # Output file name +ndigits_in = 4 # inch significant digits after '.' +ndigits_mm = 2 # mm significant digits after '.' +# mm_per_arc_segment = 0.38 # mm per arc segment +arc_tolerance = 0.00005*25.4 +n_arc_correction = 20 +inch2mm = 25.4 # inch to mm conversion scalar +verbose = False # Verbose flag to show all progress +remove_unsupported = True # Removal flag for all unsupported statements + +# Initialize parser state +gc = { 'current_xyz' : [0,0,0], + 'feed_rate' : 0, # F0 + 'motion_mode' : 'SEEK', # G00 + 'plane_axis' : [0,1,2], # G17 + 'inches_mode' : False, # G21 + 'inverse_feedrate_mode' : False, # G94 + 'absolute_mode' : True} # G90 + +def unit_conv(val) : # Converts value to mm + if gc['inches_mode'] : val *= inch2mm + return(val) + +def fout_conv(val) : # Returns converted value as rounded string for output file. + if gc['inches_mode'] : return( str(round(val/inch2mm,ndigits_in)) ) + else : return( str(round(val,ndigits_mm)) ) + +# Open g-code file +fin = open(filein,'r'); +fout = open(fileout,'w'); + +# Iterate through g-code file +l_count = 0 +for line in fin: + l_count += 1 # Iterate line counter + + # Strip comments/spaces/tabs/new line and capitalize. Comment MSG not supported. + block = re.sub('\s|\(.*?\)','',line).upper() + block = re.sub('\\\\','',block) # Strip \ block delete character + block = re.sub('%','',block) # Strip % program start/stop character + + if len(block) == 0 : # Ignore empty blocks + + print "Skipping: " + line.strip() + + else : # Process valid g-code clean block. Assumes no block delete characters or comments + + g_cmd = re.findall(r'[^0-9\.\-]+',block) # Extract block command characters + g_num = re.findall(r'[0-9\.\-]+',block) # Extract block numbers + + # G-code block error checks + # if len(g_cmd) != len(g_num) : print block; raise Exception('Invalid block. Unbalanced word and values.') + if 'N' in g_cmd : + if g_cmd[0]!='N': raise Exception('Line number must be first command in line.') + if g_cmd.count('N') > 1: raise Exception('More than one line number in block.') + g_cmd = g_cmd[1:] # Remove line number word + g_num = g_num[1:] + # Block item repeat checks? (0<=n'M'<5, G/M modal groups) + + # Initialize block state + blk = { 'next_action' : 'DEFAULT', + 'absolute_override' : False, + 'target_xyz' : deepcopy(gc['current_xyz']), + 'offset_ijk' : [0,0,0], + 'radius_mode' : False, + 'unsupported': [] } + + # Pass 1 + for cmd,num in zip(g_cmd,g_num) : + fnum = float(num) + inum = int(fnum) + if cmd is 'G' : + if inum is 0 : gc['motion_mode'] = 'SEEK' + elif inum is 1 : gc['motion_mode'] = 'LINEAR' + elif inum is 2 : gc['motion_mode'] = 'CW_ARC' + elif inum is 3 : gc['motion_mode'] = 'CCW_ARC' + elif inum is 4 : blk['next_action'] = 'DWELL' + elif inum is 17 : gc['plane_axis'] = [0,1,2] # Select XY Plane + elif inum is 18 : gc['plane_axis'] = [0,2,1] # Select XZ Plane + elif inum is 19 : gc['plane_axis'] = [1,2,0] # Select YZ Plane + elif inum is 20 : gc['inches_mode'] = True + elif inum is 21 : gc['inches_mode'] = False + elif inum in [28,30] : blk['next_action'] = 'GO_HOME' + elif inum is 53 : blk['absolute_override'] = True + elif inum is 54 : pass + elif inum is 80 : gc['motion_mode'] = 'MOTION_CANCEL' + elif inum is 90 : gc['absolute_mode'] = True + elif inum is 91 : gc['absolute_mode'] = False + elif inum is 92 : blk['next_action'] = 'SET_OFFSET' + elif inum is 93 : gc['inverse_feedrate_mode'] = True + elif inum is 94 : gc['inverse_feedrate_mode'] = False + else : + print 'Unsupported command ' + cmd + num + ' on line ' + str(l_count) + if remove_unsupported : blk['unsupported'].append(zip(g_cmd,g_num).index((cmd,num))) + elif cmd is 'M' : + if inum in [0,1] : pass # Program Pause + elif inum in [2,30,60] : pass # Program Completed + elif inum is 3 : pass # Spindle Direction 1 + elif inum is 4 : pass # Spindle Direction -1 + elif inum is 5 : pass # Spindle Direction 0 + else : + print 'Unsupported command ' + cmd + num + ' on line ' + str(l_count) + if remove_unsupported : blk['unsupported'].append(zip(g_cmd,g_num).index((cmd,num))) + elif cmd is 'T' : pass # Tool Number + + # Pass 2 + for cmd,num in zip(g_cmd,g_num) : + fnum = float(num) + if cmd is 'F' : gc['feed_rate'] = unit_conv(fnum) # Feed Rate + elif cmd in ['I','J','K'] : blk['offset_ijk'][ord(cmd)-ord('I')] = unit_conv(fnum) # Arc Center Offset + elif cmd is 'N' : pass + elif cmd is 'P' : p = fnum # Misc value parameter + elif cmd is 'R' : r = unit_conv(fnum); blk['radius_mode'] = True # Arc Radius Mode + elif cmd is 'S' : pass # Spindle Speed + elif cmd in ['X','Y','Z'] : # Target Coordinates + if (gc['absolute_mode'] | blk['absolute_override']) : + blk['target_xyz'][ord(cmd)-ord('X')] = unit_conv(fnum) + else : + blk['target_xyz'][ord(cmd)-ord('X')] += unit_conv(fnum) + + # Execute actions + if blk['next_action'] is 'GO_HOME' : + gc['current_xyz'] = deepcopy(blk['target_xyz']) # Update position + elif blk['next_action'] is 'SET_OFFSET' : + pass + elif blk['next_action'] is 'DWELL' : + if p < 0 : raise Exception('Dwell time negative.') + else : # 'DEFAULT' + if gc['motion_mode'] is 'SEEK' : + fout.write('0 '+fout_conv(gc['feed_rate'])) + fout.write(' '+fout_conv(blk['target_xyz'][0])) + fout.write(' '+fout_conv(blk['target_xyz'][1])) + fout.write(' '+fout_conv(blk['target_xyz'][2])) + fout.write('\n') + gc['current_xyz'] = deepcopy(blk['target_xyz']) # Update position + elif gc['motion_mode'] is 'LINEAR' : + fout.write('1 '+fout_conv(gc['feed_rate'])) + fout.write(' '+fout_conv(blk['target_xyz'][0])) + fout.write(' '+fout_conv(blk['target_xyz'][1])) + fout.write(' '+fout_conv(blk['target_xyz'][2])) + fout.write('\n') + gc['current_xyz'] = deepcopy(blk['target_xyz']) # Update position + elif gc['motion_mode'] in ['CW_ARC','CCW_ARC'] : + axis = gc['plane_axis'] + + # Convert radius mode to ijk mode + if blk['radius_mode'] : + x = blk['target_xyz'][axis[0]]-gc['current_xyz'][axis[0]] + y = blk['target_xyz'][axis[1]]-gc['current_xyz'][axis[1]] + if not (x==0 and y==0) : raise Exception('Same target and current XYZ not allowed in arc radius mode.') + h_x2_div_d = -sqrt(4 * r*r - x*x - y*y)/hypot(x,y) + if isnan(h_x2_div_d) : raise Exception('Floating point error in arc conversion') + if gc['motion_mode'] is 'CCW_ARC' : h_x2_div_d = -h_x2_div_d + if r < 0 : h_x2_div_d = -h_x2_div_d + blk['offset_ijk'][axis[0]] = (x-(y*h_x2_div_d))/2; + blk['offset_ijk'][axis[1]] = (y+(x*h_x2_div_d))/2; + else : + radius = sqrt(blk['offset_ijk'][axis[0]]**2+blk['offset_ijk'][axis[1]]**2) + + center_axis0 = gc['current_xyz'][axis[0]]+blk['offset_ijk'][axis[0]] + center_axis1 = gc['current_xyz'][axis[1]]+blk['offset_ijk'][axis[1]] + linear_travel = blk['target_xyz'][axis[2]]-gc['current_xyz'][axis[2]] + r_axis0 = -blk['offset_ijk'][axis[0]] + r_axis1 = -blk['offset_ijk'][axis[1]] + rt_axis0 = blk['target_xyz'][axis[0]] - center_axis0; + rt_axis1 = blk['target_xyz'][axis[1]] - center_axis1; + clockwise_sign = 1 + if gc['motion_mode'] is 'CW_ARC' : clockwise_sign = -1 + + angular_travel = atan2(r_axis0*rt_axis1-r_axis1*rt_axis0, r_axis0*rt_axis0+r_axis1*rt_axis1) + if gc['motion_mode'] is 'CW_ARC' : + if angular_travel >= 0 : + angular_travel -= 2*pi + else : + if angular_travel <= 0 : + angular_travel += 2*pi + + millimeters_of_travel = sqrt((angular_travel*radius)**2 + abs(linear_travel)**2) + + mm_per_arc_segment = sqrt(4*(2*radius*arc_tolerance-arc_tolerance**2)) + segments = int(millimeters_of_travel/mm_per_arc_segment) + print segments + print l_count + theta_per_segment = angular_travel/segments + linear_per_segment = linear_travel/segments + cos_T = 1-0.5*theta_per_segment*theta_per_segment + sin_T = theta_per_segment-theta_per_segment**3/6 + print(fout_conv(mm_per_arc_segment)) + print theta_per_segment*180/pi + + arc_target = [0,0,0] + arc_target[axis[2]] = gc['current_xyz'][axis[2]] + + count = 0 + for i in range(1,segments+1) : + if i < segments : + if count < n_arc_correction : + r_axisi = r_axis0*sin_T + r_axis1*cos_T + r_axis0 = r_axis0*cos_T - r_axis1*sin_T + r_axis1 = deepcopy(r_axisi) + count += 1 + else : + cos_Ti = cos((i-1)*theta_per_segment) + sin_Ti = sin((i-1)*theta_per_segment) + print n_arc_correction*(r_axis0 -( -blk['offset_ijk'][axis[0]]*cos_Ti + blk['offset_ijk'][axis[1]]*sin_Ti)) + print n_arc_correction*(r_axis1 -( -blk['offset_ijk'][axis[0]]*sin_Ti - blk['offset_ijk'][axis[1]]*cos_Ti)) + cos_Ti = cos(i*theta_per_segment) + sin_Ti = sin(i*theta_per_segment) + r_axis0 = -blk['offset_ijk'][axis[0]]*cos_Ti + blk['offset_ijk'][axis[1]]*sin_Ti + r_axis1 = -blk['offset_ijk'][axis[0]]*sin_Ti - blk['offset_ijk'][axis[1]]*cos_Ti + count = 0 + arc_target[axis[0]] = center_axis0 + r_axis0 + arc_target[axis[1]] = center_axis1 + r_axis1 + arc_target[axis[2]] += linear_per_segment + else : + arc_target = deepcopy(blk['target_xyz']) # Last segment at target_xyz + # Write only changed variables. + fout.write('1 '+fout_conv(gc['feed_rate'])) + fout.write(' '+fout_conv(arc_target[0])) + fout.write(' '+fout_conv(arc_target[1])) + fout.write(' '+fout_conv(arc_target[2])) + fout.write('\n') + gc['current_xyz'] = deepcopy(arc_target) # Update position + + +print 'Done!' + +# Close files +fin.close() +fout.close() \ No newline at end of file diff --git a/doc/test/test.gcode b/doc/test/test.gcode new file mode 100644 index 0000000..3d9d56c --- /dev/null +++ b/doc/test/test.gcode @@ -0,0 +1,2363 @@ +(Braid.NC Test Program) +T1M6 +G17G21 +G0X0Y0 +G0Z6.000 +G0X37.560Y12.327Z6.000 +G1Z-1.000 F300 +G1Y0.876 +X49.011 +Y12.327 +X37.560 +G0Z6.000 +G0Y0.876 +G1Z-1.000 +G1Y-10.575 +X49.011 +Y0.876 +X37.560 +G0Z6.000 +G0X49.011Y12.327 +G1Z-1.000 +G1X52.084Y15.011 +G0Z6.000 +G0X49.011Y0.876 +G1Z-1.000 +G1X52.084Y6.213 +Y15.011 +X43.286 +X37.560Y12.327 +G0Z6.000 +G0X49.011Y-10.575 +G1Z-1.000 +G1X52.084Y-2.585 +Y6.213 +X49.011Y0.876 +G0Z6.000 +G0Z20.000 +G0X0.000Y0.000 +G1X-7.098Y-39.876 +X-10.296Y-38.118 +X-10.698Y-37.897 +X-11.099Y-37.676 +X-11.894Y-37.235 +X-13.448Y-36.356 +X-13.825Y-36.137 +X-14.197Y-35.919 +X-14.923Y-35.485 +X-16.288Y-34.625 +X-16.636Y-34.395 +X-16.973Y-34.165 +X-17.613Y-33.711 +X-17.915Y-33.486 +X-18.204Y-33.262 +X-18.743Y-32.821 +X-18.992Y-32.602 +X-19.227Y-32.386 +X-19.447Y-32.171 +X-19.652Y-31.959 +X-19.843Y-31.749 +X-20.017Y-31.540 +X-20.176Y-31.335 +X-20.320Y-31.131 +X-20.447Y-30.930 +X-20.558Y-30.731 +X-20.653Y-30.535 +X-20.731Y-30.341 +X-20.793Y-30.150 +X-20.838Y-29.961 +X-20.867Y-29.776 +X-20.879Y-29.593 +X-20.875Y-29.413 +X-20.854Y-29.236 +X-20.817Y-29.062 +X-20.764Y-28.891 +X-20.695Y-28.723 +X-20.610Y-28.559 +X-20.510Y-28.397 +X-20.394Y-28.239 +X-20.263Y-28.084 +X-20.117Y-27.932 +X-19.957Y-27.784 +X-19.782Y-27.639 +X-19.594Y-27.498 +X-19.392Y-27.360 +X-19.177Y-27.226 +X-18.950Y-27.095 +X-18.710Y-26.968 +X-18.459Y-26.845 +X-18.197Y-26.725 +X-17.924Y-26.609 +X-17.641Y-26.497 +X-17.349Y-26.389 +X-16.737Y-26.183 +X-16.419Y-26.086 +X-16.094Y-25.993 +X-15.763Y-25.904 +X-15.426Y-25.818 +X-15.083Y-25.737 +X-14.736Y-25.660 +X-14.030Y-25.516 +X-13.672Y-25.451 +X-13.313Y-25.389 +X-12.953Y-25.332 +X-12.591Y-25.278 +X-12.230Y-25.228 +X-11.870Y-25.182 +X-11.511Y-25.140 +X-11.155Y-25.103 +X-10.824Y-25.071 +X-10.497Y-25.043 +X-10.173Y-25.018 +X-9.853Y-24.996 +X-9.538Y-24.978 +X-9.228Y-24.964 +X-8.924Y-24.952 +X-8.625Y-24.944 +X-8.334Y-24.940 +X-8.049Y-24.939 +X-7.772Y-24.941 +X-7.504Y-24.946 +X-7.243Y-24.955 +X-6.992Y-24.967 +X-6.750Y-24.982 +X-6.518Y-25.000 +X-6.295Y-25.022 +X-6.084Y-25.047 +X-5.883Y-25.075 +X-5.694Y-25.106 +X-5.517Y-25.140 +X-5.351Y-25.177 +X-5.197Y-25.217 +X-5.057Y-25.260 +X-4.928Y-25.306 +X-4.813Y-25.355 +X-4.711Y-25.407 +X-4.623Y-25.461 +X-4.548Y-25.518 +X-4.487Y-25.578 +X-4.439Y-25.641 +X-4.406Y-25.707 +X-4.387Y-25.775 +X-4.382Y-25.845 +X-4.392Y-25.918 +X-4.416Y-25.994 +X-4.454Y-26.072 +X-4.506Y-26.152 +X-4.572Y-26.235 +X-4.653Y-26.320 +X-4.748Y-26.407 +X-4.857Y-26.496 +X-4.980Y-26.588 +X-5.117Y-26.681 +X-5.267Y-26.777 +X-5.431Y-26.874 +X-5.798Y-27.074 +X-6.001Y-27.177 +X-6.216Y-27.282 +X-6.683Y-27.496 +X-7.755Y-27.942 +X-8.049Y-28.057 +X-8.353Y-28.173 +X-8.990Y-28.409 +X-10.362Y-28.891 +X-10.753Y-29.024 +X-11.151Y-29.158 +X-11.968Y-29.428 +X-13.658Y-29.973 +X-17.113Y-31.067 +X-17.538Y-31.202 +X-17.959Y-31.337 +X-18.788Y-31.604 +X-20.370Y-32.127 +X-20.746Y-32.255 +X-21.113Y-32.382 +X-21.817Y-32.631 +X-23.093Y-33.111 +X-23.381Y-33.226 +X-23.656Y-33.339 +X-24.164Y-33.560 +X-24.397Y-33.667 +X-24.615Y-33.772 +X-25.005Y-33.974 +X-25.177Y-34.072 +X-25.332Y-34.167 +X-25.472Y-34.259 +X-25.595Y-34.349 +X-25.701Y-34.436 +X-25.791Y-34.520 +X-25.864Y-34.601 +X-25.920Y-34.679 +X-25.960Y-34.754 +X-25.982Y-34.826 +X-25.987Y-34.895 +X-25.976Y-34.961 +X-25.948Y-35.023 +X-25.903Y-35.082 +X-25.841Y-35.138 +X-25.763Y-35.190 +X-25.669Y-35.239 +X-25.559Y-35.284 +X-25.433Y-35.325 +X-25.291Y-35.363 +X-25.134Y-35.397 +X-24.963Y-35.428 +X-24.777Y-35.455 +X-24.577Y-35.477 +X-24.367Y-35.496 +X-24.144Y-35.511 +X-23.909Y-35.522 +X-23.661Y-35.529 +X-23.403Y-35.532 +X-23.133Y-35.532 +X-22.853Y-35.527 +X-22.563Y-35.519 +X-22.263Y-35.506 +X-21.955Y-35.490 +X-21.638Y-35.470 +X-21.313Y-35.445 +X-20.981Y-35.416 +X-20.643Y-35.384 +X-20.299Y-35.347 +X-19.949Y-35.306 +X-19.595Y-35.261 +X-19.237Y-35.212 +X-18.875Y-35.158 +X-18.511Y-35.101 +X-18.145Y-35.039 +X-17.777Y-34.973 +X-17.041Y-34.829 +X-16.673Y-34.751 +X-16.306Y-34.668 +X-15.580Y-34.491 +X-15.222Y-34.396 +X-14.867Y-34.297 +X-14.172Y-34.086 +X-13.833Y-33.975 +X-13.501Y-33.859 +X-13.176Y-33.740 +X-12.858Y-33.616 +X-12.548Y-33.488 +X-12.248Y-33.357 +X-11.675Y-33.082 +X-11.404Y-32.938 +X-11.144Y-32.791 +X-10.896Y-32.639 +X-10.659Y-32.484 +X-10.435Y-32.326 +X-10.223Y-32.163 +X-10.025Y-31.997 +X-9.840Y-31.827 +X-9.669Y-31.653 +X-9.512Y-31.476 +X-9.370Y-31.295 +X-9.242Y-31.111 +X-9.130Y-30.923 +X-9.033Y-30.732 +X-8.951Y-30.538 +X-8.885Y-30.340 +X-8.835Y-30.139 +X-8.801Y-29.934 +X-8.782Y-29.727 +X-8.780Y-29.517 +X-8.794Y-29.303 +X-8.825Y-29.087 +X-8.871Y-28.867 +X-8.934Y-28.645 +X-9.006Y-28.435 +X-9.093Y-28.223 +X-9.194Y-28.008 +X-9.308Y-27.792 +X-9.435Y-27.573 +X-9.577Y-27.352 +X-9.898Y-26.903 +X-10.078Y-26.676 +X-10.271Y-26.446 +X-10.693Y-25.982 +X-10.922Y-25.748 +X-11.162Y-25.511 +X-11.675Y-25.034 +X-11.948Y-24.793 +X-12.230Y-24.550 +X-12.824Y-24.061 +X-14.114Y-23.068 +X-17.000Y-21.038 +X-17.380Y-20.782 +X-17.762Y-20.525 +X-18.532Y-20.010 +X-20.077Y-18.979 +X-20.462Y-18.721 +X-20.845Y-18.464 +X-21.602Y-17.949 +X-23.072Y-16.925 +X-23.427Y-16.671 +X-23.776Y-16.417 +X-24.454Y-15.911 +X-25.717Y-14.911 +X-26.011Y-14.664 +X-26.295Y-14.418 +X-26.833Y-13.930 +X-27.086Y-13.688 +X-27.328Y-13.447 +X-27.777Y-12.971 +X-28.000Y-12.715 +X-28.209Y-12.461 +X-28.404Y-12.210 +X-28.583Y-11.961 +X-28.746Y-11.713 +X-28.894Y-11.469 +X-29.026Y-11.226 +X-29.142Y-10.986 +X-29.242Y-10.749 +X-29.325Y-10.514 +X-29.391Y-10.281 +X-29.441Y-10.052 +X-29.475Y-9.825 +X-29.491Y-9.601 +X-29.491Y-9.380 +X-29.475Y-9.162 +X-29.441Y-8.947 +X-29.391Y-8.735 +X-29.325Y-8.526 +X-29.242Y-8.320 +X-29.144Y-8.118 +X-29.029Y-7.919 +X-28.899Y-7.723 +X-28.753Y-7.531 +X-28.592Y-7.342 +X-28.416Y-7.156 +X-28.226Y-6.974 +X-28.022Y-6.796 +X-27.804Y-6.621 +X-27.572Y-6.450 +X-27.071Y-6.119 +X-26.802Y-5.959 +X-26.522Y-5.803 +X-25.929Y-5.502 +X-25.617Y-5.358 +X-25.296Y-5.217 +X-24.628Y-4.948 +X-24.283Y-4.819 +X-23.930Y-4.694 +X-23.207Y-4.457 +X-22.838Y-4.344 +X-22.465Y-4.236 +X-21.707Y-4.031 +X-21.325Y-3.935 +X-20.941Y-3.842 +X-20.170Y-3.670 +X-19.786Y-3.590 +X-19.402Y-3.515 +X-18.642Y-3.375 +X-18.266Y-3.312 +X-17.894Y-3.252 +X-17.527Y-3.197 +X-17.164Y-3.145 +X-16.808Y-3.098 +X-16.459Y-3.055 +X-16.116Y-3.015 +X-15.781Y-2.980 +X-15.477Y-2.951 +X-15.179Y-2.925 +X-14.891Y-2.902 +X-14.611Y-2.883 +X-14.340Y-2.867 +X-14.078Y-2.855 +X-13.827Y-2.845 +X-13.586Y-2.839 +X-13.355Y-2.837 +X-13.136Y-2.837 +X-12.929Y-2.841 +X-12.733Y-2.848 +X-12.549Y-2.858 +X-12.378Y-2.871 +X-12.220Y-2.888 +X-12.074Y-2.907 +X-11.942Y-2.929 +X-11.823Y-2.955 +X-11.717Y-2.983 +X-11.626Y-3.014 +X-11.548Y-3.048 +X-11.484Y-3.084 +X-11.435Y-3.124 +X-11.400Y-3.166 +X-11.379Y-3.210 +X-11.372Y-3.258 +X-11.380Y-3.308 +X-11.403Y-3.360 +X-11.440Y-3.415 +X-11.491Y-3.472 +X-11.556Y-3.532 +X-11.636Y-3.594 +X-11.729Y-3.658 +X-11.837Y-3.724 +X-11.959Y-3.793 +X-12.094Y-3.863 +X-12.242Y-3.936 +X-12.404Y-4.010 +X-12.767Y-4.165 +X-12.967Y-4.245 +X-13.179Y-4.327 +X-13.639Y-4.495 +X-14.691Y-4.850 +X-14.979Y-4.942 +X-15.277Y-5.036 +X-15.897Y-5.225 +X-17.230Y-5.617 +X-20.146Y-6.431 +X-20.516Y-6.532 +X-20.887Y-6.634 +X-21.630Y-6.837 +X-23.106Y-7.241 +X-25.895Y-8.028 +X-26.218Y-8.123 +X-26.535Y-8.218 +X-27.145Y-8.403 +X-28.260Y-8.760 +X-28.514Y-8.846 +X-28.759Y-8.930 +X-29.214Y-9.095 +X-29.425Y-9.175 +X-29.624Y-9.253 +X-29.986Y-9.404 +X-30.149Y-9.477 +X-30.298Y-9.548 +X-30.435Y-9.617 +X-30.559Y-9.684 +X-30.669Y-9.749 +X-30.766Y-9.812 +X-30.849Y-9.872 +X-30.918Y-9.931 +X-30.974Y-9.987 +X-31.016Y-10.041 +X-31.043Y-10.092 +X-31.057Y-10.141 +X-31.057Y-10.188 +X-31.043Y-10.232 +X-31.015Y-10.273 +X-30.972Y-10.312 +X-30.916Y-10.349 +X-30.847Y-10.382 +X-30.763Y-10.413 +X-30.666Y-10.441 +X-30.556Y-10.466 +X-30.432Y-10.489 +X-30.295Y-10.508 +X-30.146Y-10.525 +X-29.984Y-10.539 +X-29.809Y-10.549 +X-29.622Y-10.557 +X-29.424Y-10.562 +X-29.214Y-10.563 +X-28.992Y-10.562 +X-28.760Y-10.557 +X-28.517Y-10.549 +X-28.242Y-10.537 +X-27.955Y-10.521 +X-27.657Y-10.501 +X-27.349Y-10.477 +X-27.031Y-10.450 +X-26.703Y-10.418 +X-26.366Y-10.383 +X-26.021Y-10.343 +X-25.668Y-10.300 +X-25.308Y-10.253 +X-24.568Y-10.146 +X-24.191Y-10.086 +X-23.808Y-10.023 +X-23.032Y-9.883 +X-22.640Y-9.807 +X-22.245Y-9.728 +X-21.453Y-9.556 +X-21.057Y-9.463 +X-20.662Y-9.367 +X-19.876Y-9.162 +X-19.487Y-9.054 +X-19.101Y-8.941 +X-18.343Y-8.704 +X-17.972Y-8.579 +X-17.606Y-8.450 +X-16.896Y-8.180 +X-16.552Y-8.040 +X-16.217Y-7.895 +X-15.574Y-7.594 +X-15.268Y-7.438 +X-14.972Y-7.277 +X-14.414Y-6.946 +X-14.153Y-6.774 +X-13.905Y-6.599 +X-13.669Y-6.420 +X-13.447Y-6.237 +X-13.239Y-6.051 +X-13.044Y-5.862 +X-12.864Y-5.669 +X-12.699Y-5.472 +X-12.549Y-5.272 +X-12.414Y-5.069 +X-12.294Y-4.862 +X-12.190Y-4.652 +X-12.102Y-4.439 +X-12.030Y-4.223 +X-11.975Y-4.003 +X-11.935Y-3.781 +X-11.911Y-3.555 +X-11.904Y-3.327 +X-11.914Y-3.096 +X-11.939Y-2.862 +X-11.981Y-2.625 +X-12.038Y-2.386 +X-12.112Y-2.144 +X-12.202Y-1.899 +X-12.300Y-1.669 +X-12.412Y-1.436 +X-12.537Y-1.201 +X-12.675Y-0.965 +X-12.826Y-0.726 +X-12.990Y-0.486 +X-13.355Y0.001 +X-13.555Y0.246 +X-13.768Y0.494 +X-14.226Y0.993 +X-15.266Y2.009 +X-15.550Y2.266 +X-15.842Y2.524 +X-16.451Y3.043 +X-17.752Y4.093 +X-20.572Y6.219 +X-20.935Y6.486 +X-21.298Y6.753 +X-22.024Y7.287 +X-23.456Y8.353 +X-23.807Y8.618 +X-24.154Y8.883 +X-24.835Y9.411 +X-26.125Y10.458 +X-26.431Y10.717 +X-26.728Y10.975 +X-27.297Y11.489 +X-27.568Y11.744 +X-27.830Y11.997 +X-28.322Y12.500 +X-28.552Y12.749 +X-28.771Y12.997 +X-28.978Y13.243 +X-29.174Y13.487 +X-29.357Y13.729 +X-29.528Y13.970 +X-29.686Y14.209 +X-29.831Y14.446 +X-29.974Y14.700 +X-30.101Y14.953 +X-30.212Y15.202 +X-30.307Y15.449 +X-30.385Y15.694 +X-30.447Y15.936 +X-30.492Y16.175 +X-30.521Y16.411 +X-30.532Y16.644 +X-30.527Y16.874 +X-30.505Y17.102 +X-30.466Y17.326 +X-30.411Y17.547 +X-30.338Y17.765 +X-30.249Y17.980 +X-30.144Y18.191 +X-30.022Y18.399 +X-29.884Y18.603 +X-29.730Y18.805 +X-29.561Y19.002 +X-29.376Y19.196 +X-29.176Y19.386 +X-28.962Y19.573 +X-28.733Y19.756 +X-28.490Y19.935 +X-28.233Y20.111 +X-27.964Y20.282 +X-27.681Y20.450 +X-27.386Y20.614 +X-27.080Y20.774 +X-26.434Y21.081 +X-26.096Y21.229 +X-25.748Y21.373 +X-25.025Y21.648 +X-24.652Y21.780 +X-24.272Y21.907 +X-23.493Y22.149 +X-23.095Y22.264 +X-22.693Y22.375 +X-21.877Y22.583 +X-21.466Y22.681 +X-21.053Y22.775 +X-20.223Y22.949 +X-19.809Y23.030 +X-19.396Y23.107 +X-18.575Y23.247 +X-18.169Y23.311 +X-17.767Y23.371 +X-16.977Y23.478 +X-16.591Y23.525 +X-16.211Y23.568 +X-15.838Y23.606 +X-15.473Y23.641 +X-15.116Y23.671 +X-14.769Y23.698 +X-14.430Y23.720 +X-14.102Y23.739 +X-13.791Y23.753 +X-13.490Y23.763 +X-13.200Y23.769 +X-12.922Y23.772 +X-12.657Y23.771 +X-12.404Y23.766 +X-12.164Y23.758 +X-11.937Y23.746 +X-11.724Y23.730 +X-11.525Y23.711 +X-11.340Y23.688 +X-11.171Y23.662 +X-11.016Y23.633 +X-10.876Y23.600 +X-10.752Y23.564 +X-10.643Y23.524 +X-10.549Y23.481 +X-10.472Y23.435 +X-10.411Y23.386 +X-10.365Y23.334 +X-10.336Y23.279 +X-10.323Y23.221 +X-10.326Y23.161 +X-10.344Y23.097 +X-10.379Y23.030 +X-10.430Y22.961 +X-10.497Y22.889 +X-10.579Y22.815 +X-10.676Y22.738 +X-10.789Y22.659 +X-10.917Y22.577 +X-11.060Y22.493 +X-11.217Y22.407 +X-11.389Y22.318 +X-11.773Y22.135 +X-11.985Y22.041 +X-12.210Y21.945 +X-12.697Y21.747 +X-12.958Y21.645 +X-13.230Y21.542 +X-13.806Y21.331 +X-15.066Y20.894 +X-15.401Y20.782 +X-15.742Y20.670 +X-16.442Y20.441 +X-17.896Y19.977 +X-20.868Y19.035 +X-21.208Y18.926 +X-21.545Y18.817 +X-22.208Y18.600 +X-23.472Y18.173 +X-23.772Y18.068 +X-24.066Y17.964 +X-24.629Y17.759 +X-25.650Y17.361 +X-25.881Y17.265 +X-26.101Y17.170 +X-26.509Y16.984 +X-26.696Y16.893 +X-26.871Y16.804 +X-27.184Y16.631 +X-27.322Y16.547 +X-27.447Y16.465 +X-27.559Y16.385 +X-27.658Y16.306 +X-27.743Y16.230 +X-27.815Y16.156 +X-27.873Y16.084 +X-27.917Y16.014 +X-27.947Y15.946 +X-27.963Y15.880 +X-27.965Y15.817 +X-27.953Y15.756 +X-27.927Y15.697 +X-27.887Y15.641 +X-27.832Y15.588 +X-27.764Y15.537 +X-27.681Y15.488 +X-27.585Y15.442 +X-27.475Y15.399 +X-27.351Y15.358 +X-27.214Y15.321 +X-27.063Y15.286 +X-26.899Y15.253 +X-26.723Y15.224 +X-26.533Y15.198 +X-26.332Y15.174 +X-26.118Y15.154 +X-25.892Y15.136 +X-25.654Y15.122 +X-25.405Y15.110 +X-25.145Y15.102 +X-24.875Y15.097 +X-24.594Y15.095 +X-24.304Y15.096 +X-24.004Y15.100 +X-23.694Y15.107 +X-23.377Y15.118 +X-23.051Y15.132 +X-22.717Y15.149 +X-22.376Y15.170 +X-21.998Y15.196 +X-21.613Y15.226 +X-21.221Y15.260 +X-20.822Y15.298 +X-20.418Y15.340 +X-20.010Y15.385 +X-19.180Y15.489 +X-18.761Y15.547 +X-18.339Y15.609 +X-17.492Y15.744 +X-17.067Y15.818 +X-16.644Y15.896 +X-15.800Y16.064 +X-15.382Y16.154 +X-14.967Y16.248 +X-14.150Y16.448 +X-13.749Y16.554 +X-13.354Y16.664 +X-12.583Y16.895 +X-12.209Y17.017 +X-11.844Y17.142 +X-11.140Y17.405 +X-10.803Y17.542 +X-10.477Y17.683 +X-9.858Y17.975 +X-9.566Y18.127 +X-9.287Y18.283 +X-9.021Y18.442 +X-8.768Y18.604 +X-8.529Y18.771 +X-8.305Y18.940 +X-8.094Y19.113 +X-7.899Y19.290 +X-7.718Y19.470 +X-7.553Y19.653 +X-7.403Y19.839 +X-7.270Y20.029 +X-7.152Y20.221 +X-7.050Y20.417 +X-6.965Y20.616 +X-6.895Y20.818 +X-6.843Y21.023 +X-6.806Y21.230 +X-6.787Y21.441 +X-6.783Y21.654 +X-6.796Y21.870 +X-6.826Y22.088 +X-6.872Y22.309 +X-6.933Y22.532 +X-7.011Y22.758 +X-7.104Y22.986 +X-7.213Y23.217 +X-7.338Y23.449 +X-7.477Y23.684 +X-7.631Y23.921 +X-7.982Y24.401 +X-8.165Y24.627 +X-8.360Y24.855 +X-8.782Y25.315 +X-9.751Y26.251 +X-10.017Y26.488 +X-10.291Y26.726 +X-10.863Y27.204 +X-12.089Y28.170 +X-12.409Y28.413 +X-12.734Y28.657 +X-13.395Y29.145 +X-14.749Y30.124 +X-17.457Y32.077 +X-17.785Y32.320 +X-18.108Y32.561 +X-18.740Y33.042 +X-19.930Y33.994 +X-20.210Y34.229 +X-20.481Y34.463 +X-20.997Y34.927 +X-21.240Y35.157 +X-21.474Y35.386 +X-21.910Y35.838 +X-22.111Y36.062 +X-22.302Y36.284 +X-22.480Y36.505 +X-22.646Y36.723 +X-22.800Y36.940 +X-22.941Y37.155 +X-23.069Y37.367 +X-23.183Y37.578 +X-23.285Y37.786 +X-23.373Y37.993 +X-23.447Y38.197 +X-23.507Y38.398 +X-23.553Y38.598 +X-23.585Y38.795 +X-23.602Y38.989 +X-23.606Y39.181 +X-23.593Y39.386 +X-23.563Y39.588 +X-23.516Y39.787 +X-23.452Y39.983 +X-23.372Y40.175 +X-23.274Y40.364 +X-23.160Y40.550 +X-23.029Y40.732 +X-22.882Y40.910 +X-22.719Y41.086 +X-22.539Y41.257 +X-22.344Y41.425 +X-22.134Y41.589 +X-21.908Y41.749 +X-21.668Y41.906 +X-21.413Y42.059 +X-21.144Y42.208 +X-20.862Y42.353 +X-20.566Y42.494 +X-20.257Y42.631 +X-19.937Y42.763 +X-19.604Y42.892 +X-18.906Y43.138 +X-18.542Y43.254 +X-18.168Y43.366 +X-17.785Y43.474 +X-17.394Y43.578 +X-16.995Y43.678 +X-16.589Y43.773 +X-15.758Y43.950 +X-15.335Y44.033 +X-14.908Y44.111 +X-14.476Y44.184 +X-14.042Y44.253 +X-13.606Y44.318 +X-13.168Y44.379 +X-12.289Y44.486 +X-11.851Y44.534 +X-11.414Y44.576 +X-10.978Y44.615 +X-10.546Y44.649 +X-10.116Y44.679 +X-9.691Y44.704 +X-9.271Y44.726 +X-8.856Y44.742 +X-8.447Y44.755 +X-8.044Y44.763 +X-7.650Y44.767 +X-7.263Y44.766 +X-6.885Y44.762 +X-6.516Y44.753 +X-6.157Y44.740 +X-5.808Y44.723 +X-5.471Y44.702 +X-5.145Y44.676 +X-4.830Y44.647 +X-4.529Y44.613 +X-4.240Y44.576 +X-3.965Y44.535 +X-3.703Y44.489 +X-3.456Y44.440 +X-3.227Y44.388 +X-3.013Y44.332 +X-2.813Y44.273 +X-2.629Y44.210 +X-2.459Y44.144 +X-2.305Y44.074 +X-2.167Y44.001 +X-2.044Y43.925 +X-1.938Y43.845 +X-1.847Y43.762 +X-1.773Y43.675 +X-1.715Y43.586 +X-1.673Y43.494 +X-1.647Y43.398 +X-1.638Y43.299 +X-1.645Y43.198 +X-1.667Y43.094 +X-1.706Y42.987 +X-1.761Y42.877 +X-1.832Y42.765 +X-1.918Y42.650 +X-2.019Y42.532 +X-2.135Y42.412 +X-2.267Y42.290 +X-2.412Y42.166 +X-2.572Y42.039 +X-2.934Y41.779 +X-3.134Y41.646 +X-3.347Y41.511 +X-3.810Y41.235 +X-4.059Y41.095 +X-4.319Y40.953 +X-4.869Y40.664 +X-6.076Y40.071 +X-8.785Y38.840 +X-9.139Y38.683 +X-9.494Y38.526 +X-10.205Y38.212 +X-11.612Y37.583 +X-14.215Y36.343 +X-14.490Y36.201 +X-14.756Y36.061 +X-15.263Y35.782 +X-15.503Y35.645 +X-15.734Y35.508 +X-16.165Y35.239 +X-16.365Y35.106 +X-16.553Y34.975 +X-16.730Y34.845 +X-16.895Y34.717 +X-17.049Y34.590 +X-17.190Y34.465 +X-17.318Y34.341 +X-17.434Y34.219 +X-17.537Y34.099 +X-17.626Y33.981 +X-17.702Y33.865 +X-17.764Y33.751 +X-17.813Y33.639 +X-17.847Y33.528 +X-17.868Y33.420 +X-17.875Y33.315 +X-17.867Y33.211 +X-17.845Y33.110 +X-17.809Y33.011 +X-17.759Y32.914 +X-17.695Y32.820 +X-17.616Y32.728 +X-17.523Y32.639 +X-17.417Y32.552 +X-17.296Y32.468 +X-17.162Y32.387 +X-17.014Y32.308 +X-16.852Y32.232 +X-16.677Y32.159 +X-16.489Y32.089 +X-16.288Y32.021 +X-16.075Y31.956 +X-15.849Y31.895 +X-15.611Y31.836 +X-15.361Y31.780 +X-15.099Y31.727 +X-14.827Y31.677 +X-14.543Y31.631 +X-14.250Y31.587 +X-13.946Y31.547 +X-13.632Y31.509 +X-13.309Y31.475 +X-12.977Y31.444 +X-12.637Y31.417 +X-12.289Y31.392 +X-11.933Y31.371 +X-11.570Y31.353 +X-11.201Y31.338 +X-10.825Y31.327 +X-10.444Y31.319 +X-10.058Y31.314 +X-9.667Y31.313 +X-9.273Y31.315 +X-8.874Y31.320 +X-8.473Y31.329 +X-8.069Y31.341 +X-7.630Y31.358 +X-7.188Y31.379 +X-6.746Y31.404 +X-6.303Y31.433 +X-5.861Y31.466 +X-5.421Y31.503 +X-4.982Y31.544 +X-4.546Y31.589 +X-4.113Y31.638 +X-3.684Y31.691 +X-3.260Y31.748 +X-2.841Y31.809 +X-2.428Y31.873 +X-2.021Y31.942 +X-1.231Y32.091 +X-0.848Y32.171 +X-0.475Y32.255 +X-0.110Y32.343 +X0.243Y32.434 +X0.587Y32.530 +X0.919Y32.629 +X1.547Y32.838 +X1.842Y32.948 +X2.124Y33.061 +X2.392Y33.178 +X2.647Y33.299 +X2.887Y33.423 +X3.112Y33.550 +X3.323Y33.681 +X3.518Y33.815 +X3.698Y33.952 +X3.862Y34.092 +X4.010Y34.236 +X4.142Y34.383 +X4.257Y34.533 +X4.356Y34.685 +X4.439Y34.841 +X4.505Y35.000 +X4.554Y35.161 +X4.586Y35.325 +X4.602Y35.492 +X4.602Y35.662 +X4.584Y35.834 +X4.551Y36.008 +X4.501Y36.185 +X4.435Y36.364 +X4.353Y36.546 +X4.255Y36.730 +X4.142Y36.916 +X4.014Y37.104 +X3.871Y37.294 +X3.713Y37.486 +X3.356Y37.876 +X3.157Y38.073 +X2.944Y38.272 +X2.483Y38.675 +X1.425Y39.496 +X1.155Y39.690 +X0.877Y39.885 +X0.300Y40.277 +X-0.925Y41.070 +X-3.540Y42.667 +X-3.871Y42.867 +X-4.202Y43.066 +X-4.858Y43.463 +X-6.136Y44.252 +X-6.444Y44.447 +X-6.748Y44.642 +X-7.337Y45.028 +X-8.430Y45.788 +X-8.682Y45.975 +X-8.926Y46.161 +X-9.383Y46.527 +X-9.596Y46.708 +X-9.799Y46.887 +X-10.170Y47.241 +X-10.337Y47.415 +X-10.493Y47.587 +X-10.636Y47.757 +X-10.766Y47.925 +X-10.884Y48.091 +X-10.988Y48.255 +X-11.078Y48.416 +X-11.155Y48.576 +X-11.218Y48.733 +X-11.267Y48.888 +X-11.301Y49.040 +X-11.322Y49.190 +X-11.328Y49.337 +X-11.319Y49.482 +X-11.296Y49.624 +X-11.259Y49.763 +X-11.207Y49.900 +X-11.140Y50.033 +X-11.059Y50.164 +X-10.963Y50.293 +X-10.853Y50.418 +X-10.729Y50.540 +X-10.591Y50.659 +X-10.439Y50.775 +X-10.276Y50.886 +X-10.100Y50.994 +X-9.912Y51.098 +X-9.710Y51.200 +X-9.496Y51.298 +X-9.270Y51.394 +X-8.783Y51.574 +X-8.522Y51.660 +X-8.250Y51.742 +X-7.968Y51.821 +X-7.675Y51.897 +X-7.372Y51.969 +X-7.059Y52.038 +X-6.407Y52.165 +X-6.068Y52.223 +X-5.722Y52.278 +X-5.368Y52.330 +X-5.006Y52.378 +X-4.639Y52.422 +X-4.265Y52.463 +X-3.886Y52.500 +X-3.501Y52.533 +X-3.112Y52.563 +X-2.719Y52.590 +X-2.322Y52.613 +X-1.923Y52.632 +X-1.521Y52.647 +X-1.116Y52.659 +X-0.711Y52.667 +X-0.304Y52.672 +X0.103Y52.673 +X0.510Y52.670 +X0.916Y52.664 +X1.321Y52.654 +X1.724Y52.640 +X2.125Y52.623 +X2.524Y52.602 +X2.919Y52.577 +X3.310Y52.549 +X3.696Y52.517 +X4.078Y52.481 +X4.455Y52.442 +X4.826Y52.400 +X5.190Y52.354 +X5.548Y52.304 +X5.898Y52.251 +X6.241Y52.194 +X6.576Y52.134 +X6.902Y52.070 +X7.219Y52.003 +X7.526Y51.933 +X7.824Y51.859 +X8.389Y51.701 +X8.656Y51.617 +X8.911Y51.530 +X9.154Y51.439 +X9.386Y51.346 +X9.606Y51.249 +X9.814Y51.149 +X10.009Y51.046 +X10.191Y50.939 +X10.374Y50.821 +X10.541Y50.698 +X10.693Y50.573 +X10.829Y50.444 +X10.948Y50.311 +X11.052Y50.175 +X11.139Y50.036 +X11.209Y49.894 +X11.263Y49.748 +X11.301Y49.600 +X11.323Y49.448 +X11.327Y49.293 +X11.316Y49.136 +X11.288Y48.975 +X11.245Y48.812 +X11.185Y48.646 +X11.109Y48.478 +X11.018Y48.307 +X10.912Y48.133 +X10.790Y47.957 +X10.654Y47.779 +X10.503Y47.598 +X10.159Y47.230 +X9.967Y47.043 +X9.762Y46.854 +X9.314Y46.470 +X8.282Y45.681 +X7.999Y45.480 +X7.707Y45.278 +X7.098Y44.870 +X5.798Y44.041 +X3.014Y42.350 +X2.662Y42.138 +X2.311Y41.925 +X1.617Y41.500 +X0.273Y40.654 +X-0.049Y40.444 +X-0.365Y40.234 +X-0.975Y39.817 +X-2.088Y38.994 +X-2.324Y38.805 +X-2.551Y38.617 +X-2.974Y38.245 +X-3.170Y38.061 +X-3.354Y37.878 +X-3.687Y37.516 +X-3.836Y37.338 +X-3.972Y37.162 +X-4.096Y36.987 +X-4.206Y36.813 +X-4.304Y36.642 +X-4.388Y36.473 +X-4.459Y36.305 +X-4.515Y36.139 +X-4.558Y35.976 +X-4.587Y35.815 +X-4.602Y35.655 +X-4.603Y35.498 +X-4.589Y35.343 +X-4.561Y35.191 +X-4.519Y35.041 +X-4.462Y34.893 +X-4.391Y34.748 +X-4.306Y34.605 +X-4.207Y34.465 +X-4.094Y34.327 +X-3.967Y34.192 +X-3.826Y34.060 +X-3.671Y33.930 +X-3.503Y33.803 +X-3.321Y33.679 +X-3.126Y33.558 +X-2.919Y33.440 +X-2.698Y33.324 +X-2.466Y33.212 +X-2.221Y33.102 +X-1.697Y32.893 +X-1.418Y32.792 +X-1.128Y32.695 +X-0.828Y32.601 +X-0.518Y32.510 +X-0.198Y32.422 +X0.131Y32.338 +X0.814Y32.178 +X1.168Y32.104 +X1.529Y32.032 +X1.897Y31.964 +X2.271Y31.899 +X2.651Y31.838 +X3.037Y31.780 +X3.822Y31.674 +X4.221Y31.626 +X4.622Y31.581 +X5.027Y31.540 +X5.434Y31.502 +X5.842Y31.468 +X6.252Y31.437 +X6.662Y31.410 +X7.072Y31.386 +X7.515Y31.363 +X7.957Y31.345 +X8.398Y31.331 +X8.835Y31.321 +X9.269Y31.315 +X9.699Y31.313 +X10.123Y31.315 +X10.543Y31.321 +X10.956Y31.330 +X11.362Y31.344 +X11.761Y31.362 +X12.152Y31.383 +X12.534Y31.409 +X12.907Y31.438 +X13.269Y31.471 +X13.622Y31.508 +X13.963Y31.549 +X14.293Y31.593 +X14.611Y31.641 +X14.916Y31.693 +X15.209Y31.749 +X15.487Y31.808 +X15.753Y31.870 +X16.003Y31.936 +X16.240Y32.006 +X16.461Y32.079 +X16.667Y32.155 +X16.858Y32.235 +X17.033Y32.318 +X17.191Y32.404 +X17.334Y32.493 +X17.460Y32.586 +X17.570Y32.681 +X17.662Y32.780 +X17.739Y32.881 +X17.798Y32.986 +X17.840Y33.093 +X17.866Y33.203 +X17.875Y33.316 +X17.867Y33.431 +X17.842Y33.549 +X17.801Y33.670 +X17.743Y33.793 +X17.669Y33.918 +X17.579Y34.045 +X17.473Y34.175 +X17.352Y34.307 +X17.215Y34.441 +X17.063Y34.577 +X16.897Y34.715 +X16.522Y34.997 +X16.314Y35.140 +X16.093Y35.285 +X15.614Y35.580 +X15.357Y35.729 +X15.089Y35.879 +X14.522Y36.184 +X13.280Y36.806 +X12.957Y36.960 +X12.628Y37.115 +X11.954Y37.427 +X10.561Y38.054 +X7.746Y39.303 +X7.404Y39.457 +X7.067Y39.611 +X6.409Y39.915 +X5.172Y40.511 +X4.883Y40.657 +X4.603Y40.802 +X4.074Y41.087 +X3.825Y41.227 +X3.588Y41.365 +X3.148Y41.636 +X2.947Y41.769 +X2.760Y41.900 +X2.585Y42.029 +X2.425Y42.156 +X2.278Y42.280 +X2.146Y42.402 +X2.028Y42.522 +X1.926Y42.639 +X1.839Y42.754 +X1.767Y42.866 +X1.711Y42.976 +X1.671Y43.083 +X1.646Y43.187 +X1.638Y43.289 +X1.645Y43.387 +X1.669Y43.483 +X1.709Y43.576 +X1.765Y43.665 +X1.838Y43.752 +X1.926Y43.835 +X2.030Y43.915 +X2.150Y43.992 +X2.286Y44.065 +X2.438Y44.135 +X2.605Y44.202 +X2.787Y44.265 +X2.984Y44.324 +X3.195Y44.380 +X3.421Y44.433 +X3.661Y44.481 +X3.915Y44.526 +X4.182Y44.568 +X4.443Y44.603 +X4.714Y44.635 +X4.996Y44.663 +X5.289Y44.688 +X5.591Y44.710 +X5.902Y44.728 +X6.223Y44.743 +X6.552Y44.754 +X6.890Y44.762 +X7.235Y44.766 +X7.587Y44.767 +X7.946Y44.764 +X8.311Y44.758 +X8.683Y44.748 +X9.059Y44.735 +X9.440Y44.717 +X9.826Y44.697 +X10.216Y44.672 +X10.608Y44.644 +X11.004Y44.613 +X11.401Y44.578 +X11.800Y44.539 +X12.602Y44.450 +X13.003Y44.400 +X13.403Y44.347 +X13.803Y44.289 +X14.201Y44.229 +X14.596Y44.164 +X14.990Y44.096 +X15.766Y43.949 +X16.148Y43.870 +X16.525Y43.787 +X17.263Y43.611 +X17.623Y43.518 +X17.976Y43.421 +X18.661Y43.217 +X18.991Y43.109 +X19.313Y42.999 +X19.626Y42.884 +X19.929Y42.767 +X20.223Y42.645 +X20.506Y42.521 +X21.041Y42.262 +X21.292Y42.127 +X21.531Y41.990 +X21.758Y41.849 +X21.974Y41.704 +X22.176Y41.557 +X22.366Y41.407 +X22.544Y41.253 +X22.708Y41.097 +X22.859Y40.937 +X22.996Y40.775 +X23.119Y40.609 +X23.229Y40.441 +X23.325Y40.269 +X23.407Y40.095 +X23.475Y39.919 +X23.529Y39.739 +X23.571Y39.541 +X23.597Y39.341 +X23.606Y39.137 +X23.599Y38.930 +X23.575Y38.721 +X23.534Y38.508 +X23.477Y38.293 +X23.404Y38.075 +X23.315Y37.855 +X23.211Y37.632 +X23.091Y37.406 +X22.955Y37.178 +X22.805Y36.948 +X22.640Y36.716 +X22.268Y36.244 +X22.061Y36.005 +X21.841Y35.765 +X21.364Y35.278 +X20.274Y34.284 +X19.975Y34.031 +X19.668Y33.778 +X19.029Y33.267 +X17.670Y32.234 +X17.316Y31.974 +X16.959Y31.713 +X16.236Y31.190 +X14.771Y30.140 +X14.405Y29.877 +X14.040Y29.614 +X13.319Y29.089 +X11.923Y28.043 +X11.588Y27.783 +X11.260Y27.524 +X10.626Y27.008 +X10.322Y26.752 +X10.027Y26.497 +X9.467Y25.990 +X9.203Y25.738 +X8.951Y25.488 +X8.482Y24.992 +X8.266Y24.747 +X8.064Y24.503 +X7.700Y24.021 +X7.550Y23.799 +X7.412Y23.578 +X7.288Y23.359 +X7.177Y23.143 +X7.079Y22.928 +X6.995Y22.715 +X6.925Y22.504 +X6.868Y22.295 +X6.826Y22.089 +X6.798Y21.884 +X6.784Y21.682 +X6.785Y21.483 +X6.800Y21.285 +X6.829Y21.091 +X6.873Y20.898 +X6.931Y20.708 +X7.003Y20.521 +X7.090Y20.336 +X7.191Y20.154 +X7.306Y19.975 +X7.434Y19.799 +X7.577Y19.625 +X7.733Y19.454 +X7.903Y19.286 +X8.085Y19.121 +X8.281Y18.959 +X8.489Y18.800 +X8.710Y18.644 +X8.943Y18.491 +X9.188Y18.341 +X9.711Y18.051 +X9.989Y17.910 +X10.278Y17.773 +X10.884Y17.508 +X11.201Y17.381 +X11.527Y17.257 +X12.203Y17.019 +X12.552Y16.905 +X12.908Y16.795 +X13.639Y16.584 +X14.012Y16.484 +X14.390Y16.387 +X15.159Y16.204 +X15.548Y16.118 +X15.940Y16.035 +X16.729Y15.880 +X17.126Y15.808 +X17.522Y15.739 +X18.315Y15.612 +X18.709Y15.554 +X19.102Y15.499 +X19.492Y15.448 +X19.879Y15.401 +X20.263Y15.357 +X20.642Y15.316 +X21.017Y15.279 +X21.387Y15.245 +X21.744Y15.215 +X22.095Y15.189 +X22.440Y15.166 +X22.778Y15.146 +X23.109Y15.129 +X23.431Y15.116 +X23.746Y15.106 +X24.052Y15.099 +X24.349Y15.095 +X24.636Y15.095 +X24.914Y15.097 +X25.181Y15.103 +X25.438Y15.112 +X25.684Y15.123 +X25.919Y15.138 +X26.142Y15.156 +X26.354Y15.177 +X26.553Y15.200 +X26.740Y15.227 +X26.914Y15.256 +X27.076Y15.288 +X27.225Y15.323 +X27.360Y15.361 +X27.482Y15.402 +X27.591Y15.445 +X27.686Y15.491 +X27.767Y15.539 +X27.835Y15.590 +X27.888Y15.643 +X27.928Y15.699 +X27.954Y15.757 +X27.965Y15.818 +X27.963Y15.881 +X27.947Y15.946 +X27.917Y16.014 +X27.873Y16.083 +X27.816Y16.155 +X27.744Y16.229 +X27.660Y16.305 +X27.562Y16.382 +X27.451Y16.462 +X27.327Y16.544 +X27.042Y16.712 +X26.881Y16.799 +X26.707Y16.887 +X26.326Y17.069 +X26.118Y17.162 +X25.900Y17.256 +X25.433Y17.449 +X24.385Y17.849 +X21.934Y18.690 +X21.575Y18.807 +X21.212Y18.925 +X20.474Y19.161 +X18.977Y19.635 +X16.040Y20.572 +X15.691Y20.686 +X15.348Y20.800 +X14.684Y21.024 +X13.458Y21.457 +X13.176Y21.562 +X12.904Y21.666 +X12.396Y21.868 +X12.159Y21.966 +X11.936Y22.063 +X11.528Y22.250 +X11.345Y22.340 +X11.176Y22.429 +X11.022Y22.515 +X10.882Y22.599 +X10.757Y22.681 +X10.648Y22.760 +X10.554Y22.836 +X10.476Y22.910 +X10.413Y22.982 +X10.367Y23.051 +X10.337Y23.117 +X10.323Y23.180 +X10.325Y23.240 +X10.344Y23.298 +X10.379Y23.352 +X10.430Y23.403 +X10.497Y23.452 +X10.580Y23.497 +X10.680Y23.538 +X10.795Y23.577 +X10.926Y23.612 +X11.072Y23.644 +X11.233Y23.673 +X11.410Y23.698 +X11.601Y23.719 +X11.807Y23.737 +X12.027Y23.751 +X12.260Y23.762 +X12.507Y23.769 +X12.767Y23.772 +X13.040Y23.771 +X13.325Y23.767 +X13.601Y23.760 +X13.887Y23.749 +X14.182Y23.734 +X14.486Y23.717 +X14.799Y23.696 +X15.120Y23.671 +X15.448Y23.643 +X15.784Y23.612 +X16.126Y23.577 +X16.475Y23.538 +X16.829Y23.496 +X17.188Y23.450 +X17.552Y23.401 +X17.921Y23.348 +X18.667Y23.232 +X19.045Y23.169 +X19.424Y23.102 +X20.187Y22.957 +X20.569Y22.879 +X20.951Y22.797 +X21.712Y22.623 +X22.090Y22.531 +X22.466Y22.435 +X23.208Y22.232 +X23.574Y22.125 +X23.935Y22.015 +X24.641Y21.784 +X24.985Y21.663 +X25.323Y21.538 +X25.977Y21.279 +X26.292Y21.144 +X26.599Y21.006 +X27.186Y20.719 +X27.465Y20.571 +X27.734Y20.419 +X27.993Y20.264 +X28.241Y20.106 +X28.477Y19.944 +X28.702Y19.779 +X29.116Y19.440 +X29.305Y19.266 +X29.481Y19.088 +X29.644Y18.908 +X29.794Y18.724 +X29.930Y18.538 +X30.053Y18.349 +X30.162Y18.156 +X30.258Y17.961 +X30.339Y17.763 +X30.406Y17.563 +X30.459Y17.359 +X30.498Y17.153 +X30.522Y16.944 +X30.532Y16.733 +X30.528Y16.519 +X30.510Y16.303 +X30.474Y16.066 +X30.421Y15.826 +X30.352Y15.583 +X30.266Y15.337 +X30.164Y15.089 +X30.046Y14.839 +X29.912Y14.586 +X29.762Y14.330 +X29.597Y14.073 +X29.417Y13.813 +X29.014Y13.287 +X28.791Y13.021 +X28.555Y12.753 +X28.044Y12.211 +X27.769Y11.938 +X27.483Y11.663 +X26.879Y11.109 +X25.553Y9.985 +X22.573Y7.693 +X22.183Y7.405 +X21.791Y7.116 +X21.005Y6.537 +X19.442Y5.382 +X19.058Y5.094 +X18.678Y4.806 +X17.931Y4.232 +X16.513Y3.095 +X16.178Y2.813 +X15.852Y2.532 +X15.229Y1.974 +X14.933Y1.697 +X14.649Y1.422 +X14.115Y0.876 +X13.867Y0.606 +X13.632Y0.337 +X13.411Y0.071 +X13.204Y-0.194 +X13.010Y-0.457 +X12.832Y-0.717 +X12.668Y-0.976 +X12.520Y-1.232 +X12.395Y-1.469 +X12.284Y-1.703 +X12.188Y-1.936 +X12.105Y-2.167 +X12.036Y-2.395 +X11.982Y-2.621 +X11.942Y-2.844 +X11.916Y-3.065 +X11.905Y-3.284 +X11.908Y-3.500 +X11.926Y-3.713 +X11.959Y-3.924 +X12.005Y-4.132 +X12.067Y-4.338 +X12.142Y-4.540 +X12.232Y-4.740 +X12.336Y-4.937 +X12.453Y-5.131 +X12.584Y-5.322 +X12.729Y-5.510 +X12.887Y-5.695 +X13.059Y-5.877 +X13.243Y-6.055 +X13.439Y-6.231 +X13.648Y-6.403 +X13.869Y-6.572 +X14.101Y-6.738 +X14.345Y-6.901 +X14.599Y-7.060 +X14.864Y-7.216 +X15.423Y-7.518 +X15.717Y-7.664 +X16.020Y-7.806 +X16.650Y-8.080 +X16.976Y-8.212 +X17.309Y-8.340 +X17.994Y-8.586 +X18.344Y-8.704 +X18.700Y-8.818 +X19.423Y-9.035 +X20.902Y-9.426 +X21.276Y-9.515 +X21.650Y-9.600 +X22.396Y-9.759 +X22.767Y-9.833 +X23.137Y-9.903 +X23.868Y-10.033 +X24.228Y-10.092 +X24.584Y-10.148 +X24.936Y-10.200 +X25.282Y-10.249 +X25.622Y-10.294 +X25.956Y-10.336 +X26.604Y-10.408 +X26.910Y-10.438 +X27.208Y-10.465 +X27.498Y-10.489 +X27.779Y-10.509 +X28.051Y-10.527 +X28.313Y-10.540 +X28.565Y-10.551 +X28.807Y-10.558 +X29.038Y-10.562 +X29.258Y-10.563 +X29.466Y-10.561 +X29.663Y-10.556 +X29.848Y-10.547 +X30.020Y-10.536 +X30.180Y-10.522 +X30.327Y-10.504 +X30.462Y-10.484 +X30.583Y-10.461 +X30.690Y-10.435 +X30.784Y-10.406 +X30.865Y-10.374 +X30.932Y-10.340 +X30.984Y-10.303 +X31.023Y-10.263 +X31.048Y-10.221 +X31.058Y-10.176 +X31.055Y-10.128 +X31.037Y-10.078 +X31.006Y-10.026 +X30.960Y-9.971 +X30.900Y-9.914 +X30.827Y-9.855 +X30.739Y-9.794 +X30.638Y-9.730 +X30.524Y-9.664 +X30.396Y-9.596 +X30.255Y-9.527 +X30.100Y-9.455 +X29.754Y-9.306 +X29.562Y-9.228 +X29.359Y-9.149 +X28.917Y-8.986 +X27.902Y-8.642 +X27.624Y-8.553 +X27.336Y-8.462 +X26.734Y-8.278 +X25.440Y-7.896 +X22.592Y-7.100 +X22.191Y-6.990 +X21.787Y-6.879 +X20.978Y-6.659 +X19.371Y-6.218 +X18.976Y-6.109 +X18.584Y-6.000 +X17.814Y-5.783 +X16.349Y-5.360 +X16.002Y-5.257 +X15.664Y-5.155 +X15.016Y-4.954 +X14.708Y-4.856 +X14.410Y-4.759 +X13.850Y-4.569 +X13.589Y-4.477 +X13.340Y-4.387 +X12.883Y-4.212 +X12.675Y-4.127 +X12.482Y-4.045 +X12.140Y-3.886 +X11.991Y-3.810 +X11.859Y-3.737 +X11.741Y-3.665 +X11.640Y-3.597 +X11.555Y-3.531 +X11.486Y-3.467 +X11.433Y-3.406 +X11.397Y-3.348 +X11.377Y-3.293 +X11.373Y-3.241 +X11.386Y-3.191 +X11.415Y-3.145 +X11.460Y-3.101 +X11.522Y-3.061 +X11.600Y-3.024 +X11.694Y-2.990 +X11.803Y-2.959 +X11.928Y-2.932 +X12.068Y-2.908 +X12.223Y-2.887 +X12.393Y-2.870 +X12.578Y-2.856 +X12.776Y-2.846 +X12.989Y-2.840 +X13.214Y-2.837 +X13.453Y-2.838 +X13.704Y-2.842 +X13.967Y-2.850 +X14.242Y-2.862 +X14.527Y-2.878 +X14.824Y-2.897 +X15.130Y-2.921 +X15.425Y-2.946 +X15.727Y-2.975 +X16.037Y-3.007 +X16.353Y-3.042 +X16.676Y-3.081 +X17.005Y-3.124 +X17.678Y-3.219 +X18.021Y-3.272 +X18.368Y-3.329 +X19.071Y-3.452 +X19.426Y-3.519 +X19.783Y-3.590 +X20.499Y-3.742 +X20.857Y-3.823 +X21.214Y-3.908 +X21.925Y-4.088 +X22.277Y-4.183 +X22.626Y-4.282 +X23.314Y-4.491 +X23.652Y-4.600 +X23.984Y-4.713 +X24.632Y-4.949 +X24.947Y-5.073 +X25.254Y-5.199 +X25.846Y-5.463 +X26.129Y-5.600 +X26.404Y-5.740 +X26.669Y-5.884 +X26.925Y-6.031 +X27.170Y-6.181 +X27.405Y-6.334 +X27.842Y-6.650 +X28.043Y-6.813 +X28.232Y-6.979 +X28.409Y-7.149 +X28.573Y-7.321 +X28.725Y-7.496 +X28.863Y-7.674 +X28.989Y-7.855 +X29.101Y-8.039 +X29.199Y-8.226 +X29.283Y-8.416 +X29.353Y-8.609 +X29.409Y-8.804 +X29.451Y-9.002 +X29.479Y-9.203 +X29.492Y-9.406 +X29.491Y-9.612 +X29.475Y-9.820 +X29.445Y-10.031 +X29.401Y-10.244 +X29.342Y-10.459 +X29.269Y-10.677 +X29.181Y-10.897 +X29.080Y-11.119 +X28.964Y-11.344 +X28.823Y-11.589 +X28.666Y-11.837 +X28.493Y-12.087 +X28.305Y-12.340 +X28.102Y-12.594 +X27.883Y-12.851 +X27.403Y-13.370 +X27.142Y-13.632 +X26.868Y-13.897 +X26.281Y-14.430 +X24.969Y-15.514 +X24.615Y-15.788 +X24.252Y-16.064 +X23.501Y-16.617 +X21.917Y-17.734 +X18.570Y-19.985 +X18.148Y-20.266 +X17.729Y-20.547 +X16.897Y-21.108 +X15.285Y-22.222 +X14.896Y-22.498 +X14.515Y-22.774 +X13.775Y-23.321 +X12.409Y-24.400 +X12.095Y-24.666 +X11.792Y-24.930 +X11.223Y-25.453 +X10.958Y-25.712 +X10.707Y-25.968 +X10.246Y-26.475 +X10.038Y-26.725 +X9.845Y-26.973 +X9.667Y-27.218 +X9.505Y-27.461 +X9.359Y-27.702 +X9.229Y-27.940 +X9.115Y-28.175 +X9.017Y-28.407 +X8.938Y-28.632 +X8.874Y-28.855 +X8.827Y-29.074 +X8.796Y-29.291 +X8.781Y-29.505 +X8.782Y-29.716 +X8.799Y-29.923 +X8.833Y-30.128 +X8.882Y-30.329 +X8.947Y-30.527 +X9.028Y-30.722 +X9.125Y-30.914 +X9.236Y-31.102 +X9.363Y-31.286 +X9.505Y-31.467 +X9.661Y-31.645 +X9.832Y-31.819 +X10.016Y-31.989 +X10.214Y-32.155 +X10.425Y-32.318 +X10.649Y-32.477 +X10.885Y-32.633 +X11.393Y-32.932 +X11.664Y-33.076 +X11.945Y-33.215 +X12.536Y-33.483 +X12.846Y-33.611 +X13.163Y-33.735 +X13.821Y-33.971 +X14.160Y-34.082 +X14.504Y-34.190 +X15.209Y-34.393 +X15.568Y-34.488 +X15.930Y-34.579 +X16.661Y-34.748 +X17.029Y-34.827 +X17.397Y-34.901 +X17.766Y-34.971 +X18.134Y-35.037 +X18.501Y-35.099 +X18.865Y-35.157 +X19.585Y-35.260 +X19.940Y-35.305 +X20.290Y-35.346 +X20.634Y-35.383 +X20.973Y-35.416 +X21.305Y-35.444 +X21.630Y-35.469 +X21.947Y-35.490 +X22.256Y-35.506 +X22.556Y-35.519 +X22.847Y-35.527 +X23.128Y-35.532 +X23.398Y-35.532 +X23.657Y-35.529 +X23.904Y-35.522 +X24.140Y-35.511 +X24.363Y-35.496 +X24.559Y-35.479 +X24.745Y-35.459 +X24.918Y-35.435 +X25.079Y-35.408 +X25.228Y-35.378 +X25.364Y-35.345 +X25.487Y-35.309 +X25.597Y-35.269 +X25.694Y-35.227 +X25.777Y-35.182 +X25.847Y-35.133 +X25.902Y-35.082 +X25.944Y-35.028 +X25.972Y-34.972 +X25.986Y-34.912 +X25.986Y-34.850 +X25.971Y-34.785 +X25.942Y-34.717 +X25.899Y-34.647 +X25.842Y-34.575 +X25.771Y-34.499 +X25.685Y-34.422 +X25.586Y-34.342 +X25.472Y-34.260 +X25.345Y-34.175 +X25.204Y-34.088 +X24.883Y-33.909 +X24.702Y-33.815 +X24.509Y-33.720 +X24.085Y-33.525 +X23.855Y-33.424 +X23.613Y-33.321 +X23.095Y-33.112 +X21.933Y-32.673 +X19.198Y-31.738 +X12.952Y-29.747 +X12.533Y-29.612 +X12.117Y-29.477 +X11.302Y-29.208 +X9.752Y-28.680 +X9.384Y-28.550 +X9.026Y-28.422 +X8.340Y-28.168 +X7.104Y-27.677 +X6.826Y-27.559 +X6.561Y-27.442 +X6.073Y-27.213 +X5.850Y-27.102 +X5.643Y-26.992 +X5.272Y-26.780 +X5.111Y-26.677 +X4.965Y-26.577 +X4.835Y-26.479 +X4.721Y-26.383 +X4.623Y-26.290 +X4.542Y-26.199 +X4.478Y-26.112 +X4.430Y-26.026 +X4.398Y-25.944 +X4.384Y-25.865 +X4.385Y-25.788 +X4.403Y-25.714 +X4.438Y-25.644 +X4.489Y-25.576 +X4.556Y-25.512 +X4.639Y-25.450 +X4.738Y-25.392 +X4.852Y-25.337 +X4.981Y-25.286 +X5.126Y-25.238 +X5.285Y-25.193 +X5.458Y-25.152 +X5.646Y-25.115 +X5.847Y-25.080 +X6.061Y-25.050 +X6.287Y-25.023 +X6.527Y-25.000 +X6.777Y-24.980 +X7.040Y-24.964 +X7.313Y-24.952 +X7.596Y-24.944 +X7.889Y-24.939 +X8.191Y-24.939 +X8.502Y-24.942 +X8.820Y-24.949 +X9.146Y-24.960 +X9.478Y-24.975 +X9.817Y-24.994 +X10.161Y-25.017 +X10.510Y-25.044 +X10.839Y-25.072 +X11.171Y-25.104 +X11.506Y-25.140 +X11.842Y-25.179 +X12.180Y-25.221 +X12.519Y-25.267 +X13.196Y-25.370 +X13.534Y-25.427 +X13.870Y-25.487 +X14.204Y-25.550 +X14.535Y-25.617 +X14.864Y-25.688 +X15.188Y-25.762 +X15.824Y-25.920 +X16.134Y-26.004 +X16.438Y-26.092 +X16.736Y-26.183 +X17.027Y-26.277 +X17.311Y-26.375 +X17.587Y-26.476 +X18.113Y-26.689 +X18.362Y-26.800 +X18.602Y-26.914 +X18.832Y-27.032 +X19.052Y-27.152 +X19.260Y-27.276 +X19.457Y-27.403 +X19.643Y-27.534 +X19.817Y-27.667 +X19.979Y-27.803 +X20.128Y-27.943 +X20.264Y-28.085 +X20.387Y-28.230 +X20.497Y-28.379 +X20.594Y-28.530 +X20.676Y-28.684 +X20.745Y-28.840 +X20.800Y-29.000 +X20.841Y-29.162 +X20.867Y-29.327 +X20.879Y-29.494 +X20.876Y-29.664 +X20.859Y-29.837 +X20.828Y-30.012 +X20.781Y-30.189 +X20.721Y-30.369 +X20.645Y-30.551 +X20.556Y-30.735 +X20.452Y-30.922 +X20.333Y-31.110 +X20.201Y-31.301 +X20.055Y-31.494 +X19.894Y-31.688 +X19.721Y-31.885 +X19.533Y-32.084 +X19.120Y-32.486 +X18.893Y-32.690 +X18.655Y-32.896 +X18.142Y-33.311 +X17.874Y-33.517 +X17.595Y-33.724 +X17.007Y-34.142 +X15.720Y-34.991 +X12.798Y-36.727 +X12.410Y-36.947 +X12.018Y-37.167 +X11.225Y-37.607 +X9.621Y-38.487 +X6.455Y-40.236 +X6.074Y-40.452 +X5.697Y-40.668 +X4.960Y-41.096 +X3.566Y-41.941 +X3.237Y-42.149 +X2.916Y-42.356 +X2.302Y-42.766 +X1.194Y-43.568 +X0.944Y-43.764 +X0.706Y-43.959 +X0.264Y-44.342 +X0.062Y-44.531 +X-0.128Y-44.718 +X-0.469Y-45.085 +X-0.620Y-45.266 +X-0.758Y-45.444 +X-0.882Y-45.620 +X-0.993Y-45.794 +X-1.090Y-45.965 +X-1.173Y-46.134 +X-1.243Y-46.301 +X-1.298Y-46.465 +X-1.342Y-46.640 +X-1.371Y-46.812 +X-1.382Y-46.980 +X-1.378Y-47.146 +X-1.357Y-47.308 +X-1.320Y-47.467 +X-1.267Y-47.622 +X-1.198Y-47.775 +X-1.114Y-47.923 +X-1.014Y-48.068 +X-0.899Y-48.210 +X-0.769Y-48.348 +X-0.625Y-48.482 +X-0.466Y-48.612 +X-0.293Y-48.739 +X-0.107Y-48.862 +X0.093Y-48.981 +X0.305Y-49.097 +X0.530Y-49.208 +X0.766Y-49.315 +X1.014Y-49.419 +X1.273Y-49.518 +X1.822Y-49.705 +X2.110Y-49.792 +X2.407Y-49.875 +X2.713Y-49.954 +X3.026Y-50.029 +X3.346Y-50.100 +X3.672Y-50.166 +X4.004Y-50.229 +X4.341Y-50.287 +X4.683Y-50.340 +X5.028Y-50.390 +X5.376Y-50.435 +X5.727Y-50.476 +X6.080Y-50.513 +X6.433Y-50.545 +X6.787Y-50.573 +X7.141Y-50.597 +X7.494Y-50.617 +X7.845Y-50.632 +X8.194Y-50.643 +X8.540Y-50.650 +X8.882Y-50.652 +X9.220Y-50.651 +X9.553Y-50.645 +X9.881Y-50.635 +X10.202Y-50.620 +X10.516Y-50.602 +X10.823Y-50.579 +X11.122Y-50.552 +X11.412Y-50.521 +X11.693Y-50.486 +X11.964Y-50.447 +X12.225Y-50.404 +X12.475Y-50.356 +X12.714Y-50.305 +X12.941Y-50.250 +X13.155Y-50.191 +X13.357Y-50.128 +X13.546Y-50.061 +X13.721Y-49.990 +X13.882Y-49.916 +X14.020Y-49.843 +X14.144Y-49.767 +X14.256Y-49.688 +X14.355Y-49.605 +X14.441Y-49.520 +X14.513Y-49.431 +X14.571Y-49.340 +X14.615Y-49.245 +X14.645Y-49.148 +X14.662Y-49.048 +X14.664Y-48.944 +X14.651Y-48.838 +X14.625Y-48.730 +X14.584Y-48.618 +X14.529Y-48.504 +X14.459Y-48.387 +X14.376Y-48.268 +X14.278Y-48.146 +X14.166Y-48.022 +X14.040Y-47.896 +X13.900Y-47.767 +X13.747Y-47.635 +X13.399Y-47.366 +X13.206Y-47.228 +X12.999Y-47.088 +X12.548Y-46.803 +X11.504Y-46.209 +X11.215Y-46.057 +X10.916Y-45.902 +X10.287Y-45.589 +X8.923Y-44.948 +X5.870Y-43.614 +X5.468Y-43.444 +X5.063Y-43.274 +X4.246Y-42.931 +X2.603Y-42.244 +X-0.599Y-40.871 +X-1.025Y-40.682 +X-1.444Y-40.493 +X-2.257Y-40.118 +X-3.764Y-39.380 +X-4.113Y-39.199 +X-4.450Y-39.019 +X-5.084Y-38.665 +X-5.380Y-38.490 +X-5.662Y-38.317 +X-6.181Y-37.977 +X-6.417Y-37.810 +X-6.637Y-37.645 +X-6.841Y-37.483 +X-7.028Y-37.322 +X-7.199Y-37.165 +X-7.352Y-37.009 +X-7.488Y-36.856 +X-7.607Y-36.706 +X-7.708Y-36.559 +X-7.791Y-36.414 +X-7.856Y-36.272 +X-7.904Y-36.133 +X-7.934Y-35.997 +X-7.946Y-35.864 +X-7.941Y-35.734 +X-7.918Y-35.608 +X-7.877Y-35.484 +X-7.819Y-35.364 +X-7.744Y-35.247 +X-7.652Y-35.134 +X-7.543Y-35.024 +X-7.418Y-34.918 +X-7.277Y-34.815 +X-7.121Y-34.716 +X-6.949Y-34.620 +X-6.762Y-34.528 +X-6.560Y-34.440 +X-6.345Y-34.356 +X-6.116Y-34.276 +X-5.874Y-34.199 +X-5.620Y-34.127 +X-5.353Y-34.058 +X-5.076Y-33.993 +X-4.787Y-33.933 +X-4.489Y-33.876 +X-4.181Y-33.824 +X-3.864Y-33.775 +X-3.539Y-33.731 +X-3.207Y-33.691 +X-2.867Y-33.655 +X-2.522Y-33.623 +X-2.172Y-33.596 +X-1.817Y-33.572 +X-1.458Y-33.553 +X-1.096Y-33.538 +X-0.732Y-33.528 +X-0.366Y-33.521 +X0.000Y-33.519 +G0Z6.000 +G0X37.560Y12.327Z6.000 +G1Z-1.000 +G1Y0.876 +X49.011 +Y12.327 +X37.560 +G0Z6.000 +G0Y0.876 +G1Z-1.000 +G1Y-10.575 +X49.011 +Y0.876 +X37.560 +G0Z6.000 +G0X49.011Y12.327 +G1Z-1.000 +G1X52.084Y15.011 +G0Z6.000 +G0X49.011Y0.876 +G1Z-1.000 +G1X52.084Y6.213 +Y15.011 +X43.286 +X37.560Y12.327 +G0Z6.000 +G0X49.011Y-10.575 +G1Z-1.000 +G1X52.084Y-2.585 +Y6.213 +X49.011Y0.876 +G0Z6.000 +G0Z20.000 +G0X0.000Y0.000 +M30 diff --git a/eeprom.h b/eeprom.h index cffbcde..d93deee 100644 --- a/eeprom.h +++ b/eeprom.h @@ -1,7 +1,7 @@ #ifndef eeprom_h #define eeprom_h -char eeprom_get_char(unsigned int addr); +unsigned char eeprom_get_char(unsigned int addr); void eeprom_put_char(unsigned int addr, unsigned char new_value); void memcpy_to_eeprom_with_checksum(unsigned int destination, char *source, unsigned int size); int memcpy_from_eeprom_with_checksum(char *destination, unsigned int source, unsigned int size); diff --git a/gcode.c b/gcode.c index b17a34a..a68373e 100644 --- a/gcode.c +++ b/gcode.c @@ -2,8 +2,8 @@ gcode.c - rs274/ngc parser. Part of Grbl + Copyright (c) 2011-2014 Sungeun K. Jeon Copyright (c) 2009-2011 Simen Svale Skogsrud - Copyright (c) 2011-2013 Sungeun K. Jeon Grbl is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -19,62 +19,64 @@ along with Grbl. If not, see . */ -/* This code is inspired by the Arduino GCode Interpreter by Mike Ellery and the NIST RS274/NGC Interpreter - by Kramer, Proctor and Messina. */ - -#include "gcode.h" -#include -#include "nuts_bolts.h" -#include +#include "system.h" #include "settings.h" +#include "protocol.h" +#include "gcode.h" #include "motion_control.h" #include "spindle_control.h" #include "coolant_control.h" -#include "errno.h" -#include "protocol.h" +#include "probe.h" #include "report.h" +// NOTE: Max line number is defined by the g-code standard to be 99999. It seems to be an +// arbitrary value, and some GUIs may require more. So we increased it based on a max safe +// value when converting a float (7.2 digit precision)s to an integer. +#define MAX_LINE_NUMBER 9999999 + +#define AXIS_COMMAND_NONE 0 +#define AXIS_COMMAND_NON_MODAL 1 +#define AXIS_COMMAND_MOTION_MODE 2 +#define AXIS_COMMAND_TOOL_LENGTH_OFFSET 3 // *Undefined but required + // Declare gc extern struct -parser_state_t gc; +parser_state_t gc_state; +parser_block_t gc_block; -#define FAIL(status) gc.status_code = status; +#define FAIL(status) return(status); -static int next_statement(char *letter, float *float_ptr, char *line, uint8_t *char_counter); - -static void select_plane(uint8_t axis_0, uint8_t axis_1, uint8_t axis_2) -{ - gc.plane_axis_0 = axis_0; - gc.plane_axis_1 = axis_1; - gc.plane_axis_2 = axis_2; -} void gc_init() { - memset(&gc, 0, sizeof(gc)); - gc.feed_rate = settings.default_feed_rate; - select_plane(X_AXIS, Y_AXIS, Z_AXIS); - gc.absolute_mode = true; + memset(&gc_state, 0, sizeof(gc_state)); // Load default G54 coordinate system. - if (!(settings_read_coord_data(gc.coord_select,gc.coord_system))) { + if (!(settings_read_coord_data(gc_state.modal.coord_select,gc_state.coord_system))) { report_status_message(STATUS_SETTING_READ_FAIL); } } + // Sets g-code parser position in mm. Input in steps. Called by the system abort and hard // limit pull-off routines. -void gc_set_current_position(int32_t x, int32_t y, int32_t z) +void gc_sync_position() { - gc.position[X_AXIS] = x/settings.steps_per_mm[X_AXIS]; - gc.position[Y_AXIS] = y/settings.steps_per_mm[Y_AXIS]; - gc.position[Z_AXIS] = z/settings.steps_per_mm[Z_AXIS]; + uint8_t i; + for (i=0; i 255, variable type must be changed to uint16_t. - float inverse_feed_rate = -1; // negative inverse_feed_rate means no inverse_feed_rate specified - uint8_t absolute_override = false; // true(1) = absolute motion for this block only {G53} - uint8_t non_modal_action = NON_MODAL_NONE; // Tracks the actions of modal group 0 (non-modal) - - float target[N_AXIS], offset[N_AXIS]; - clear_vector(target); // XYZ(ABC) axes parameters. - clear_vector(offset); // IJK Arc offsets are incremental. Value of zero indicates no change. + + while (line[char_counter] != 0) { // Loop until no more g-code words in line. - gc.status_code = STATUS_OK; - - /* Pass 1: Commands and set all modes. Check for modal group violations. - NOTE: Modal group numbers are defined in Table 4 of NIST RS274-NGC v3, pg.20 */ - uint8_t group_number = MODAL_GROUP_NONE; - while(next_statement(&letter, &value, line, &char_counter)) { + // Import the next g-code word, expecting a letter followed by a value. Otherwise, error out. + letter = line[char_counter]; + if((letter < 'A') || (letter > 'Z')) { FAIL(STATUS_EXPECTED_COMMAND_LETTER); } // [Expected word letter] + char_counter++; + if (!read_float(line, &char_counter, &value)) { FAIL(STATUS_BAD_NUMBER_FORMAT); } // [Expected word value] + + // Convert values to smaller uint8 significand and mantissa values for parsing this word. + // NOTE: Mantissa is multiplied by 100 to catch non-integer command values. This is more + // accurate than the NIST gcode requirement of x10 when used for commands, but not quite + // accurate enough for value words that require integers to within 0.0001. This should be + // a good enough comprimise and catch most all non-integer errors. To make it compliant, + // we would simply need to change the mantissa to int16, but this add compiled flash space. + // Maybe update this later. int_value = trunc(value); + mantissa = round(100*(value - int_value)); // Compute mantissa for Gxx.x commands. + // NOTE: Rounding must be used to catch small floating point errors. + + // Check if the g-code word is supported or errors due to modal group violations or has + // been repeated in the g-code block. If ok, update the command or record its value. switch(letter) { + + /* 'G' and 'M' Command Words: Parse commands and check for modal group violations. + NOTE: Modal group numbers are defined in Table 4 of NIST RS274-NGC v3, pg.20 */ + case 'G': - // Set modal group values + // Determine 'G' command and its modal group switch(int_value) { - case 4: case 10: case 28: case 30: case 53: case 92: group_number = MODAL_GROUP_0; break; - case 0: case 1: case 2: case 3: case 80: group_number = MODAL_GROUP_1; break; - case 17: case 18: case 19: group_number = MODAL_GROUP_2; break; - case 90: case 91: group_number = MODAL_GROUP_3; break; - case 93: case 94: group_number = MODAL_GROUP_5; break; - case 20: case 21: group_number = MODAL_GROUP_6; break; - case 54: case 55: case 56: case 57: case 58: case 59: group_number = MODAL_GROUP_12; break; - } - // Set 'G' commands - switch(int_value) { - case 0: gc.motion_mode = MOTION_MODE_SEEK; break; - case 1: gc.motion_mode = MOTION_MODE_LINEAR; break; - case 2: gc.motion_mode = MOTION_MODE_CW_ARC; break; - case 3: gc.motion_mode = MOTION_MODE_CCW_ARC; break; - case 4: non_modal_action = NON_MODAL_DWELL; break; - case 10: non_modal_action = NON_MODAL_SET_COORDINATE_DATA; break; - case 17: select_plane(X_AXIS, Y_AXIS, Z_AXIS); break; - case 18: select_plane(X_AXIS, Z_AXIS, Y_AXIS); break; - case 19: select_plane(Y_AXIS, Z_AXIS, X_AXIS); break; - case 20: gc.inches_mode = true; break; - case 21: gc.inches_mode = false; break; - case 28: case 30: - int_value = trunc(10*value); // Multiply by 10 to pick up Gxx.1 + case 10: case 28: case 30: case 92: + // Check for G10/28/30/92 being called with G0/1/2/3/38 on same block. + // * G43.1 is also an axis command but is not explicitly defined this way. + if (mantissa == 0) { // Ignore G28.1, G30.1, and G92.1 + if (axis_command) { FAIL(STATUS_GCODE_AXIS_COMMAND_CONFLICT); } // [Axis word/command conflict] + axis_command = AXIS_COMMAND_NON_MODAL; + } + // No break. Continues to next line. + case 4: case 53: + word_bit = MODAL_GROUP_G0; switch(int_value) { - case 280: non_modal_action = NON_MODAL_GO_HOME_0; break; - case 281: non_modal_action = NON_MODAL_SET_HOME_0; break; - case 300: non_modal_action = NON_MODAL_GO_HOME_1; break; - case 301: non_modal_action = NON_MODAL_SET_HOME_1; break; - default: FAIL(STATUS_UNSUPPORTED_STATEMENT); + case 4: gc_block.non_modal_command = NON_MODAL_DWELL; break; // G4 + case 10: gc_block.non_modal_command = NON_MODAL_SET_COORDINATE_DATA; break; // G10 + case 28: + switch(mantissa) { + case 0: gc_block.non_modal_command = NON_MODAL_GO_HOME_0; break; // G28 + case 10: gc_block.non_modal_command = NON_MODAL_SET_HOME_0; break; // G28.1 + default: FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); // [Unsupported G28.x command] + } + mantissa = 0; // Set to zero to indicate valid non-integer G command. + break; + case 30: + switch(mantissa) { + case 0: gc_block.non_modal_command = NON_MODAL_GO_HOME_1; break; // G30 + case 10: gc_block.non_modal_command = NON_MODAL_SET_HOME_1; break; // G30.1 + default: FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); // [Unsupported G30.x command] + } + mantissa = 0; // Set to zero to indicate valid non-integer G command. + break; + case 53: gc_block.non_modal_command = NON_MODAL_ABSOLUTE_OVERRIDE; break; // G53 + case 92: + switch(mantissa) { + case 0: gc_block.non_modal_command = NON_MODAL_SET_COORDINATE_OFFSET; break; // G92 + case 10: gc_block.non_modal_command = NON_MODAL_RESET_COORDINATE_OFFSET; break; // G92.1 + default: FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); // [Unsupported G92.x command] + } + mantissa = 0; // Set to zero to indicate valid non-integer G command. + break; } break; - case 53: absolute_override = true; break; - case 54: case 55: case 56: case 57: case 58: case 59: - gc.coord_select = int_value-54; - break; - case 80: gc.motion_mode = MOTION_MODE_CANCEL; break; - case 90: gc.absolute_mode = true; break; - case 91: gc.absolute_mode = false; break; - case 92: - int_value = trunc(10*value); // Multiply by 10 to pick up G92.1 + case 0: case 1: case 2: case 3: case 38: + // Check for G0/1/2/3/38 being called with G10/28/30/92 on same block. + // * G43.1 is also an axis command but is not explicitly defined this way. + if (axis_command) { FAIL(STATUS_GCODE_AXIS_COMMAND_CONFLICT); } // [Axis word/command conflict] + axis_command = AXIS_COMMAND_MOTION_MODE; + // No break. Continues to next line. + case 80: + word_bit = MODAL_GROUP_G1; switch(int_value) { - case 920: non_modal_action = NON_MODAL_SET_COORDINATE_OFFSET; break; - case 921: non_modal_action = NON_MODAL_RESET_COORDINATE_OFFSET; break; - default: FAIL(STATUS_UNSUPPORTED_STATEMENT); + case 0: gc_block.modal.motion = MOTION_MODE_SEEK; break; // G0 + case 1: gc_block.modal.motion = MOTION_MODE_LINEAR; break; // G1 + case 2: gc_block.modal.motion = MOTION_MODE_CW_ARC; break; // G2 + case 3: gc_block.modal.motion = MOTION_MODE_CCW_ARC; break; // G3 + case 38: + switch(mantissa) { + case 20: gc_block.modal.motion = MOTION_MODE_PROBE; break; // G38.2 + // NOTE: If G38.3+ are enabled, change mantissa variable type to uint16_t. + // case 30: gc_block.modal.motion = MOTION_MODE_PROBE_NO_ERROR; break; // G38.3 Not supported. + // case 40: // Not supported. + // case 50: // Not supported. + default: FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); // [Unsupported G38.x command] + } + mantissa = 0; // Set to zero to indicate valid non-integer G command. + break; + case 80: gc_block.modal.motion = MOTION_MODE_NONE; break; // G80 + } + break; + case 17: case 18: case 19: + word_bit = MODAL_GROUP_G2; + switch(int_value) { + case 17: gc_block.modal.plane_select = PLANE_SELECT_XY; break; + case 18: gc_block.modal.plane_select = PLANE_SELECT_ZX; break; + case 19: gc_block.modal.plane_select = PLANE_SELECT_YZ; break; } break; - case 93: gc.inverse_feed_rate_mode = true; break; - case 94: gc.inverse_feed_rate_mode = false; break; - default: FAIL(STATUS_UNSUPPORTED_STATEMENT); - } - break; + case 90: case 91: + word_bit = MODAL_GROUP_G3; + if (int_value == 90) { gc_block.modal.distance = DISTANCE_MODE_ABSOLUTE; } // G90 + else { gc_block.modal.distance = DISTANCE_MODE_INCREMENTAL; } // G91 + break; + case 93: case 94: + word_bit = MODAL_GROUP_G5; + if (int_value == 93) { gc_block.modal.feed_rate = FEED_RATE_MODE_INVERSE_TIME; } // G93 + else { gc_block.modal.feed_rate = FEED_RATE_MODE_UNITS_PER_MIN; } // G94 + break; + case 20: case 21: + word_bit = MODAL_GROUP_G6; + if (int_value == 20) { gc_block.modal.units = UNITS_MODE_INCHES; } // G20 + else { gc_block.modal.units = UNITS_MODE_MM; } // G21 + break; + case 43: case 49: + word_bit = MODAL_GROUP_G8; + // NOTE: The NIST g-code standard vaguely states that when a tool length offset is changed, + // there cannot be any axis motion or coordinate offsets updated. Meaning G43, G43.1, and G49 + // all are explicit axis commands, regardless if they require axis words or not. + if (axis_command) { FAIL(STATUS_GCODE_AXIS_COMMAND_CONFLICT); } // [Axis word/command conflict] } + axis_command = AXIS_COMMAND_TOOL_LENGTH_OFFSET; + if (int_value == 49) { // G49 + gc_block.modal.tool_length = TOOL_LENGTH_OFFSET_CANCEL; + } else if (mantissa == 10) { // G43.1 + gc_block.modal.tool_length = TOOL_LENGTH_OFFSET_ENABLE_DYNAMIC; + } else { FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); } // [Unsupported G43.x command] + mantissa = 0; // Set to zero to indicate valid non-integer G command. + break; + case 54: case 55: case 56: case 57: case 58: case 59: + // NOTE: G59.x are not supported. (But their int_values would be 60, 61, and 62.) + word_bit = MODAL_GROUP_G12; + gc_block.modal.coord_select = int_value-54; // Shift to array indexing. + break; + default: FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); // [Unsupported G command] + } + if (mantissa > 0) { FAIL(STATUS_GCODE_COMMAND_VALUE_NOT_INTEGER); } // [Unsupported or invalid Gxx.x command] + // Check for more than one command per modal group violations in the current block + // NOTE: Variable 'word_bit' is always assigned, if the command is valid. + if ( bit_istrue(command_words,bit(word_bit)) ) { FAIL(STATUS_GCODE_MODAL_GROUP_VIOLATION); } + command_words |= bit(word_bit); + break; + case 'M': - // Set modal group values + + // Determine 'M' command and its modal group + if (mantissa > 0) { FAIL(STATUS_GCODE_COMMAND_VALUE_NOT_INTEGER); } // [No Mxx.x commands] switch(int_value) { - case 0: case 1: case 2: case 30: group_number = MODAL_GROUP_4; break; - case 3: case 4: case 5: group_number = MODAL_GROUP_7; break; - } - // Set 'M' commands - switch(int_value) { - case 0: gc.program_flow = PROGRAM_FLOW_PAUSED; break; // Program pause - case 1: break; // Optional stop not supported. Ignore. - case 2: case 30: gc.program_flow = PROGRAM_FLOW_COMPLETED; break; // Program end and reset - case 3: gc.spindle_direction = 1; break; - case 4: gc.spindle_direction = -1; break; - case 5: gc.spindle_direction = 0; break; - #ifdef ENABLE_M7 - case 7: gc.coolant_mode = COOLANT_MIST_ENABLE; break; - #endif - case 8: gc.coolant_mode = COOLANT_FLOOD_ENABLE; break; - case 9: gc.coolant_mode = COOLANT_DISABLE; break; - default: FAIL(STATUS_UNSUPPORTED_STATEMENT); + case 0: case 1: case 2: case 30: + word_bit = MODAL_GROUP_M4; + switch(int_value) { + case 0: gc_block.modal.program_flow = PROGRAM_FLOW_PAUSED; break; // Program pause + case 1: break; // Optional stop not supported. Ignore. + case 2: case 30: gc_block.modal.program_flow = PROGRAM_FLOW_COMPLETED; break; // Program end and reset + } + break; + case 3: case 4: case 5: + word_bit = MODAL_GROUP_M7; + switch(int_value) { + case 3: gc_block.modal.spindle = SPINDLE_ENABLE_CW; break; + case 4: gc_block.modal.spindle = SPINDLE_ENABLE_CCW; break; + case 5: gc_block.modal.spindle = SPINDLE_DISABLE; break; + } + break; + #ifdef ENABLE_M7 + case 7: + #endif + case 8: case 9: + word_bit = MODAL_GROUP_M8; + switch(int_value) { + #ifdef ENABLE_M7 + case 7: gc_block.modal.coolant = COOLANT_MIST_ENABLE; break; + #endif + case 8: gc_block.modal.coolant = COOLANT_FLOOD_ENABLE; break; + case 9: gc_block.modal.coolant = COOLANT_DISABLE; break; + } + break; + default: FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); // [Unsupported M command] } + + // Check for more than one command per modal group violations in the current block + // NOTE: Variable 'word_bit' is always assigned, if the command is valid. + if ( bit_istrue(command_words,bit(word_bit)) ) { FAIL(STATUS_GCODE_MODAL_GROUP_VIOLATION); } + command_words |= bit(word_bit); break; - } - // Check for modal group multiple command violations in the current block - if (group_number) { - if ( bit_istrue(modal_group_words,bit(group_number)) ) { - FAIL(STATUS_MODAL_GROUP_VIOLATION); - } else { - bit_true(modal_group_words,bit(group_number)); - } - group_number = MODAL_GROUP_NONE; // Reset for next command. - } + + // NOTE: All remaining letters assign values. + default: + + /* Non-Command Words: This initial parsing phase only checks for repeats of the remaining + legal g-code words and stores their value. Error-checking is performed later since some + words (I,J,K,L,P,R) have multiple connotations and/or depend on the issued commands. */ + switch(letter){ + // case 'A': // Not supported + // case 'B': // Not supported + // case 'C': // Not supported + // case 'D': // Not supported + case 'F': word_bit = WORD_F; gc_block.values.f = value; break; + // case 'H': // Not supported + case 'I': word_bit = WORD_I; gc_block.values.ijk[X_AXIS] = value; ijk_words |= (1< MAX_LINE_NUMBER) { FAIL(STATUS_GCODE_INVALID_LINE_NUMBER); } // [Exceeds max line number] + } + // bit_false(value_words,bit(WORD_N)); // NOTE: Single-meaning value word. Set at end of error-checking. + + // Track for unused words at the end of error-checking. + // NOTE: Single-meaning value words are removed all at once at the end of error-checking, because + // they are always used when present. This was done to save a few bytes of flash. For clarity, the + // single-meaning value words may be removed as they are used. Also, axis words are treated in the + // same way. If there is an explicit/implicit axis command, XYZ words are always used and are + // are removed at the end of error-checking. + + // [1. Comments ]: MSG's NOT SUPPORTED. Comment handling performed by protocol. + + // [2. Set feed rate mode ]: G93 F word missing with G1,G2/3 active, implicitly or explicitly. Feed rate + // is not defined after switching to G94 from G93. + if (gc_block.modal.feed_rate == FEED_RATE_MODE_INVERSE_TIME) { // = G93 + // NOTE: G38 can also operate in inverse time, but is undefined as an error. Missing F word check added here. + if (axis_command == AXIS_COMMAND_MOTION_MODE) { + if ((gc_block.modal.motion != MOTION_MODE_NONE) || (gc_block.modal.motion != MOTION_MODE_SEEK)) { + if (bit_isfalse(value_words,bit(WORD_F))) { FAIL(STATUS_GCODE_UNDEFINED_FEED_RATE); } // [F word missing] + } + } + // NOTE: It seems redundant to check for an F word to be passed after switching from G94 to G93. We would + // accomplish the exact same thing if the feed rate value is always reset to zero and undefined after each + // inverse time block, since the commands that use this value already perform undefined checks. This would + // also allow other commands, following this switch, to execute and not error out needlessly. This code is + // combined with the above feed rate mode and the below set feed rate error-checking. + + // [3. Set feed rate ]: F is negative (done.) + // - In inverse time mode: Always implicitly zero the feed rate value before and after block completion. + // NOTE: If in G93 mode or switched into it from G94, just keep F value as initialized zero or passed F word + // value in the block. If no F word is passed with a motion command that requires a feed rate, this will error + // out in the motion modes error-checking. However, if no F word is passed with NO motion command that requires + // a feed rate, we simply move on and the state feed rate value gets updated to zero and remains undefined. + } else { // = G94 + // - In units per mm mode: If F word passed, ensure value is in mm/min, otherwise push last state value. + if (gc_state.modal.feed_rate == FEED_RATE_MODE_UNITS_PER_MIN) { // Last state is also G94 + if (bit_istrue(value_words,bit(WORD_F))) { + if (gc_block.modal.units == UNITS_MODE_INCHES) { gc_block.values.f *= MM_PER_INCH; } + } else { + gc_block.values.f = gc_state.feed_rate; // Push last state feed rate + } + } // Else, switching to G94 from G93, so don't push last state feed rate. Its undefined or the passed F word value. + } + // bit_false(value_words,bit(WORD_F)); // NOTE: Single-meaning value word. Set at end of error-checking. + + // [4. Set spindle speed ]: S is negative (done.) + if (bit_isfalse(value_words,bit(WORD_S))) { gc_block.values.s = gc_state.spindle_speed; } + // bit_false(value_words,bit(WORD_S)); // NOTE: Single-meaning value word. Set at end of error-checking. + + // [5. Select tool ]: NOT SUPPORTED. Only tracks value. T is negative (done.) Not an integer. Greater than max tool value. + // bit_false(value_words,bit(WORD_T)); // NOTE: Single-meaning value word. Set at end of error-checking. + + // [6. Change tool ]: N/A + // [7. Spindle control ]: N/A + // [8. Coolant control ]: N/A + // [9. Enable/disable feed rate or spindle overrides ]: NOT SUPPORTED. + + // [10. Dwell ]: P value missing. P is negative (done.) NOTE: See below. + if (gc_block.non_modal_command == NON_MODAL_DWELL) { + if (bit_isfalse(value_words,bit(WORD_P))) { FAIL(STATUS_GCODE_VALUE_WORD_MISSING); } // [P word missing] + bit_false(value_words,bit(WORD_P)); + } + + // [11. Set active plane ]: N/A + switch (gc_block.modal.plane_select) { + case PLANE_SELECT_XY: + axis_0 = X_AXIS; + axis_1 = Y_AXIS; + axis_linear = Z_AXIS; + break; + case PLANE_SELECT_ZX: + axis_0 = Z_AXIS; + axis_1 = X_AXIS; + axis_linear = Y_AXIS; + break; + default: // case PLANE_SELECT_YZ: + axis_0 = Y_AXIS; + axis_1 = Z_AXIS; + axis_linear = X_AXIS; + } + + // [12. Set length units ]: N/A + // Pre-convert XYZ coordinate values to millimeters, if applicable. + uint8_t idx; + if (gc_block.modal.units == UNITS_MODE_INCHES) { + for (idx=0; idx N_COORDINATE_SYSTEM) { FAIL(STATUS_GCODE_UNSUPPORTED_COORD_SYS); } // [Greater than N sys] + if (gc_state.modal.coord_select != gc_block.modal.coord_select) { + if (!(settings_read_coord_data(gc_block.modal.coord_select,coordinate_data))) { FAIL(STATUS_SETTING_READ_FAIL); } + } } - // [G4,G10,G28,G30,G92,G92.1]: Perform dwell, set coordinate system data, homing, or set axis offsets. - // NOTE: These commands are in the same modal group, hence are mutually exclusive. G53 is in this - // modal group and do not effect these actions. - switch (non_modal_action) { - case NON_MODAL_DWELL: - if (p < 0) { // Time cannot be negative. - FAIL(STATUS_INVALID_STATEMENT); - } else { - // Ignore dwell in check gcode modes - if (sys.state != STATE_CHECK_MODE) { mc_dwell(p); } + // [16. Set path control mode ]: NOT SUPPORTED. + // [17. Set distance mode ]: N/A. G90.1 and G91.1 NOT SUPPORTED. + // [18. Set retract mode ]: NOT SUPPORTED. + + // [19. Remaining non-modal actions ]: Check go to predefined position, set G10, or set axis offsets. + // NOTE: We need to separate the non-modal commands that are axis word-using (G10/G28/G30/G92), as these + // commands all treat axis words differently. G10 as absolute offsets or computes current position as + // the axis value, G92 similarly to G10 L20, and G28/30 as an intermediate target position that observes + // all the current coordinate system and G92 offsets. + switch (gc_block.non_modal_command) { + case NON_MODAL_SET_COORDINATE_DATA: + // [G10 Errors]: L missing and is not 2 or 20. P word missing. (Negative P value done.) + // [G10 L2 Errors]: R word NOT SUPPORTED. P value not 0 to nCoordSys(max 9). Axis words missing. + // [G10 L20 Errors]: P must be 0 to nCoordSys(max 9). Axis words missing. + if (!axis_words) { FAIL(STATUS_GCODE_NO_AXIS_WORDS) }; // [No axis words] + if (bit_isfalse(value_words,((1< N_COORDINATE_SYSTEM) { FAIL(STATUS_GCODE_UNSUPPORTED_COORD_SYS); } // [Greater than N sys] + if (gc_block.values.l != 20) { + if (gc_block.values.l == 2) { + if (bit_istrue(value_words,bit(WORD_R))) { FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); } // [G10 L2 R not supported] + } else { FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); } // [Unsupported L] } - break; - case NON_MODAL_SET_COORDINATE_DATA: - int_value = trunc(p); // Convert p value to int. - if ((l != 2 && l != 20) || (int_value < 0 || int_value > N_COORDINATE_SYSTEM)) { // L2 and L20. P1=G54, P2=G55, ... - FAIL(STATUS_UNSUPPORTED_STATEMENT); - } else if (!axis_words && l==2) { // No axis words. - FAIL(STATUS_INVALID_STATEMENT); - } else { - if (int_value > 0) { int_value--; } // Adjust P1-P6 index to EEPROM coordinate data indexing. - else { int_value = gc.coord_select; } // Index P0 as the active coordinate system - float coord_data[N_AXIS]; - if (!settings_read_coord_data(int_value,coord_data)) { return(STATUS_SETTING_READ_FAIL); } - uint8_t i; + bit_false(value_words,(bit(WORD_L)|bit(WORD_P))); + + // Load EEPROM coordinate data and pre-calculate the new coordinate data. + if (int_value > 0) { int_value--; } // Adjust P1-P6 index to EEPROM coordinate data indexing. + else { int_value = gc_block.modal.coord_select; } // Index P0 as the active coordinate system + if (!settings_read_coord_data(int_value,parameter_data)) { FAIL(STATUS_SETTING_READ_FAIL); } // [EEPROM read fail] + for (idx=0; idx C -----------------+--------------- T <- [x,y] - | <------ d/2 ---->| - - C - Current position - T - Target position - O - center of circle that pass through both C and T - d - distance from C to T - r - designated radius - h - distance from center of CT to O - - Expanding the equations: + gc_block.values.xyz[idx] = gc_state.coord_offset[idx]; + } + } + break; + + default: + + // At this point, the rest of the explicit axis commands treat the axis values as the traditional + // target position with the coordinate system offsets, G92 offsets, absolute override, and distance + // modes applied. This includes the motion mode commands. We can now pre-compute the target position. + // NOTE: Tool offsets may be appended to these conversions when/if this feature is added. + if (axis_command != AXIS_COMMAND_TOOL_LENGTH_OFFSET ) { // TLO block any axis command. + if (axis_words) { + for (idx=0; idx sqrt(x^2 + y^2) - h -> sqrt(4 * r^2 - x^2 - y^2)/2 - i -> (x - (y * sqrt(4 * r^2 - x^2 - y^2)) / sqrt(x^2 + y^2)) / 2 - j -> (y + (x * sqrt(4 * r^2 - x^2 - y^2)) / sqrt(x^2 + y^2)) / 2 - - Which can be written: - - i -> (x - (y * sqrt(4 * r^2 - x^2 - y^2))/sqrt(x^2 + y^2))/2 - j -> (y + (x * sqrt(4 * r^2 - x^2 - y^2))/sqrt(x^2 + y^2))/2 - - Which we for size and speed reasons optimize to: + d^2 == x^2 + y^2 + h^2 == r^2 - (d/2)^2 + i == x/2 - y/d*h + j == y/2 + x/d*h - h_x2_div_d = sqrt(4 * r^2 - x^2 - y^2)/sqrt(x^2 + y^2) - i = (x - (y * h_x2_div_d))/2 - j = (y + (x * h_x2_div_d))/2 + O <- [i,j] + - | + r - | + - | + - | h + - | + [0,0] -> C -----------------+--------------- T <- [x,y] + | <------ d/2 ---->| - */ - - // Calculate the change in position along each selected axis - float x = target[gc.plane_axis_0]-gc.position[gc.plane_axis_0]; - float y = target[gc.plane_axis_1]-gc.position[gc.plane_axis_1]; - - clear_vector(offset); + C - Current position + T - Target position + O - center of circle that pass through both C and T + d - distance from C to T + r - designated radius + h - distance from center of CT to O + + Expanding the equations: + + d -> sqrt(x^2 + y^2) + h -> sqrt(4 * r^2 - x^2 - y^2)/2 + i -> (x - (y * sqrt(4 * r^2 - x^2 - y^2)) / sqrt(x^2 + y^2)) / 2 + j -> (y + (x * sqrt(4 * r^2 - x^2 - y^2)) / sqrt(x^2 + y^2)) / 2 + + Which can be written: + + i -> (x - (y * sqrt(4 * r^2 - x^2 - y^2))/sqrt(x^2 + y^2))/2 + j -> (y + (x * sqrt(4 * r^2 - x^2 - y^2))/sqrt(x^2 + y^2))/2 + + Which we for size and speed reasons optimize to: + + h_x2_div_d = sqrt(4 * r^2 - x^2 - y^2)/sqrt(x^2 + y^2) + i = (x - (y * h_x2_div_d))/2 + j = (y + (x * h_x2_div_d))/2 + */ + // First, use h_x2_div_d to compute 4*h^2 to check if it is negative or r is smaller // than d. If so, the sqrt of a negative number is complex and error out. - float h_x2_div_d = 4 * r*r - x*x - y*y; - if (h_x2_div_d < 0) { FAIL(STATUS_ARC_RADIUS_ERROR); return(gc.status_code); } + float h_x2_div_d = 4.0 * gc_block.values.r*gc_block.values.r - x*x - y*y; + + if (h_x2_div_d < 0) { FAIL(STATUS_GCODE_ARC_RADIUS_ERROR); } // [Arc radius error] + // Finish computing h_x2_div_d. - h_x2_div_d = -sqrt(h_x2_div_d)/hypot(x,y); // == -(h * 2 / d) + h_x2_div_d = -sqrt(h_x2_div_d)/hypot_f(x,y); // == -(h * 2 / d) // Invert the sign of h_x2_div_d if the circle is counter clockwise (see sketch below) - if (gc.motion_mode == MOTION_MODE_CCW_ARC) { h_x2_div_d = -h_x2_div_d; } - + if (gc_block.modal.motion == MOTION_MODE_CCW_ARC) { h_x2_div_d = -h_x2_div_d; } + /* The counter clockwise circle lies to the left of the target direction. When offset is positive, the left hand circle will be generated - when it is negative the right hand circle is generated. - - - T <-- Target position - - ^ - Clockwise circles with this center | Clockwise circles with this center will have - will have > 180 deg of angular travel | < 180 deg of angular travel, which is a good thing! - \ | / - center of arc when h_x2_div_d is positive -> x <----- | -----> x <- center of arc when h_x2_div_d is negative - | - | - - C <-- Current position */ - - + + T <-- Target position + + ^ + Clockwise circles with this center | Clockwise circles with this center will have + will have > 180 deg of angular travel | < 180 deg of angular travel, which is a good thing! + \ | / + center of arc when h_x2_div_d is positive -> x <----- | -----> x <- center of arc when h_x2_div_d is negative + | + | + + C <-- Current position + */ // Negative R is g-code-alese for "I want a circle with more than 180 degrees of travel" (go figure!), // even though it is advised against ever generating such circles in a single line of g-code. By // inverting the sign of h_x2_div_d the center of the circles is placed on the opposite side of the line of // travel and thus we get the unadvisably long arcs as prescribed. - if (r < 0) { + if (gc_block.values.r < 0) { h_x2_div_d = -h_x2_div_d; - r = -r; // Finished with r. Set to positive for mc_arc + gc_block.values.r = -gc_block.values.r; // Finished with r. Set to positive for mc_arc } // Complete the operation by calculating the actual center of the arc - offset[gc.plane_axis_0] = 0.5*(x-(y*h_x2_div_d)); - offset[gc.plane_axis_1] = 0.5*(y+(x*h_x2_div_d)); - - } else { // Arc Center Format Offset Mode - r = hypot(offset[gc.plane_axis_0], offset[gc.plane_axis_1]); // Compute arc radius for mc_arc - } + gc_block.values.ijk[axis_0] = 0.5*(x-(y*h_x2_div_d)); + gc_block.values.ijk[axis_1] = 0.5*(y+(x*h_x2_div_d)); - // Set clockwise/counter-clockwise sign for mc_arc computations - uint8_t isclockwise = false; - if (gc.motion_mode == MOTION_MODE_CW_ARC) { isclockwise = true; } - - // Trace the arc - mc_arc(gc.position, target, offset, gc.plane_axis_0, gc.plane_axis_1, gc.plane_axis_2, - (gc.inverse_feed_rate_mode) ? inverse_feed_rate : gc.feed_rate, gc.inverse_feed_rate_mode, - r, isclockwise); - } - break; + } else { // Arc Center Format Offset Mode + if (!(ijk_words & (bit(axis_0)|bit(axis_1)))) { FAIL(STATUS_GCODE_NO_OFFSETS_IN_PLANE); } // [No offsets in plane] + bit_false(value_words,(bit(WORD_I)|bit(WORD_J)|bit(WORD_K))); + + // Convert IJK values to proper units. + if (gc_block.modal.units == UNITS_MODE_INCHES) { + for (idx=0; idx 0.005) { + if (delta_r > 0.5) { FAIL(STATUS_GCODE_INVALID_TARGET); } // [Arc definition error] > 0.5mm + if (delta_r > (0.001*gc_block.values.r)) { FAIL(STATUS_GCODE_INVALID_TARGET); } // [Arc definition error] > 0.005mm AND 0.1% radius + } + } + break; + case MOTION_MODE_PROBE: + // [G38 Errors]: Target is same current. No axis words. Cutter compensation is enabled. Feed rate + // is undefined. Probe is triggered. + if (!axis_words) { FAIL(STATUS_GCODE_NO_AXIS_WORDS); } // [No axis words] + if (gc_check_same_position(gc_state.position, gc_block.values.xyz)) { FAIL(STATUS_GCODE_INVALID_TARGET); } // [Invalid target] + if (probe_get_state()) { FAIL(STATUS_GCODE_PROBE_TRIGGERED); } // [Probe triggered] + break; + } } - - // Report any errors. - if (gc.status_code) { return(gc.status_code); } - - // As far as the parser is concerned, the position is now == target. In reality the - // motion control system might still be processing the action and the real tool position - // in any intermediate location. - memcpy(gc.position, target, sizeof(target)); // gc.position[] = target[]; } + // [21. Program flow ]: No error check required. + + // [0. Non-specific error-checks]: Complete unused value words check, i.e. IJK used when in arc + // radius mode, or axis words that aren't used in the block. + bit_false(value_words,(bit(WORD_N)|bit(WORD_F)|bit(WORD_S)|bit(WORD_T))); // Remove single-meaning value words. + if (axis_command) { bit_false(value_words,(bit(WORD_X)|bit(WORD_Y)|bit(WORD_Z))); } // Remove axis words. + if (value_words) { FAIL(STATUS_GCODE_UNUSED_WORDS); } // [Unused words] + + + /* ------------------------------------------------------------------------------------- + STEP 4: EXECUTE!! + Assumes that all error-checking has been completed and no failure modes exist. We just + need to update the state and execute the block according to the order-of-execution. + */ + + // [1. Comments feedback ]: NOT SUPPORTED + + // [2. Set feed rate mode ]: + gc_state.modal.feed_rate = gc_block.modal.feed_rate; + + // [3. Set feed rate ]: + gc_state.feed_rate = gc_block.values.f; // Always copy this value. See feed rate error-checking. + + // [4. Set spindle speed ]: + if (gc_state.spindle_speed != gc_block.values.s) { + gc_state.spindle_speed = gc_block.values.s; + + // Update running spindle only if not in check mode and not already enabled. + if (gc_state.modal.spindle != SPINDLE_DISABLE) { spindle_run(gc_state.modal.spindle, gc_state.spindle_speed); } + } + + // [5. Select tool ]: NOT SUPPORTED + + // [6. Change tool ]: NOT SUPPORTED + + // [7. Spindle control ]: + if (gc_state.modal.spindle != gc_block.modal.spindle) { + gc_state.modal.spindle = gc_block.modal.spindle; + // Update spindle control and apply spindle speed when enabling it in this block. + spindle_run(gc_state.modal.spindle, gc_state.spindle_speed); + } + + // [8. Coolant control ]: + if (gc_state.modal.coolant != gc_block.modal.coolant) { + gc_state.modal.coolant = gc_block.modal.coolant; + coolant_run(gc_state.modal.coolant); + } + + // [9. Enable/disable feed rate or spindle overrides ]: NOT SUPPORTED + + // [10. Dwell ]: + if (gc_block.non_modal_command == NON_MODAL_DWELL) { mc_dwell(gc_block.values.p); } + + // [11. Set active plane ]: + gc_state.modal.plane_select = gc_block.modal.plane_select; + + // [12. Set length units ]: + gc_state.modal.units = gc_block.modal.units; + + // [13. Cutter radius compensation ]: NOT SUPPORTED + + // [14. Cutter length compensation ]: G43.1 and G49 supported. G43 NOT SUPPORTED. + // NOTE: If G43 were supported, its operation wouldn't be any different from G43.1 in terms + // of execution. The error-checking step would simply load the offset value into the correct + // axis of the block XYZ value array. + if (axis_command == AXIS_COMMAND_TOOL_LENGTH_OFFSET ) { // Indicates a change. + gc_state.modal.tool_length = gc_block.modal.tool_length; + if (gc_state.modal.tool_length == TOOL_LENGTH_OFFSET_ENABLE_DYNAMIC) { // G43.1 + gc_state.tool_length_offset = gc_block.values.xyz[TOOL_LENGTH_OFFSET_AXIS]; + } else { // G49 + gc_state.tool_length_offset = 0.0; + } + } + + // [15. Coordinate system selection ]: + if (gc_state.modal.coord_select != gc_block.modal.coord_select) { + gc_state.modal.coord_select = gc_block.modal.coord_select; + memcpy(gc_state.coord_system,coordinate_data,sizeof(coordinate_data)); + } + + // [16. Set path control mode ]: NOT SUPPORTED + + // [17. Set distance mode ]: + gc_state.modal.distance = gc_block.modal.distance; + + // [18. Set retract mode ]: NOT SUPPORTED + + // [19. Go to predefined position, Set G10, or Set axis offsets ]: + switch(gc_block.non_modal_command) { + case NON_MODAL_SET_COORDINATE_DATA: + +// TODO: See if I can clean up this int_value. + int_value = trunc(gc_block.values.p); // Convert p value to int. + if (int_value > 0) { int_value--; } // Adjust P1-P6 index to EEPROM coordinate data indexing. + else { int_value = gc_state.modal.coord_select; } // Index P0 as the active coordinate system + + settings_write_coord_data(int_value,parameter_data); + // Update system coordinate system if currently active. + if (gc_state.modal.coord_select == int_value) { memcpy(gc_state.coord_system,parameter_data,sizeof(parameter_data)); } + break; + case NON_MODAL_GO_HOME_0: case NON_MODAL_GO_HOME_1: + // Move to intermediate position before going home. Obeys current coordinate system and offsets + // and absolute and incremental modes. + if (axis_command) { + #ifdef USE_LINE_NUMBERS + mc_line(gc_block.values.xyz, -1.0, false, gc_block.values.n); + #else + mc_line(gc_block.values.xyz, -1.0, false); + #endif + } + #ifdef USE_LINE_NUMBERS + mc_line(parameter_data, -1.0, false, gc_block.values.n); + #else + mc_line(parameter_data, -1.0, false); + #endif + memcpy(gc_state.position, parameter_data, sizeof(parameter_data)); + break; + case NON_MODAL_SET_HOME_0: + settings_write_coord_data(SETTING_INDEX_G28,gc_state.position); + break; + case NON_MODAL_SET_HOME_1: + settings_write_coord_data(SETTING_INDEX_G30,gc_state.position); + break; + case NON_MODAL_SET_COORDINATE_OFFSET: + memcpy(gc_state.coord_offset,gc_block.values.xyz,sizeof(gc_block.values.xyz)); + break; + case NON_MODAL_RESET_COORDINATE_OFFSET: + clear_vector(gc_state.coord_offset); // Disable G92 offsets by zeroing offset vector. + break; + } + + + // [20. Motion modes ]: + // NOTE: Commands G10,G28,G30,G92 lock out and prevent axis words from use in motion modes. + // Enter motion modes only if there are axis words or a motion mode command word in the block. + gc_state.modal.motion = gc_block.modal.motion; + if (gc_state.modal.motion != MOTION_MODE_NONE) { + if (axis_command == AXIS_COMMAND_MOTION_MODE) { + switch (gc_state.modal.motion) { + case MOTION_MODE_SEEK: + #ifdef USE_LINE_NUMBERS + mc_line(gc_block.values.xyz, -1.0, false, gc_block.values.n); + #else + mc_line(gc_block.values.xyz, -1.0, false); + #endif + break; + case MOTION_MODE_LINEAR: + #ifdef USE_LINE_NUMBERS + mc_line(gc_block.values.xyz, gc_state.feed_rate, gc_state.modal.feed_rate, gc_block.values.n); + #else + mc_line(gc_block.values.xyz, gc_state.feed_rate, gc_state.modal.feed_rate); + #endif + break; + case MOTION_MODE_CW_ARC: case MOTION_MODE_CCW_ARC: + #ifdef USE_LINE_NUMBERS + mc_arc(gc_state.position, gc_block.values.xyz, gc_block.values.ijk, gc_block.values.r, + gc_state.feed_rate, gc_state.modal.feed_rate, axis_0, axis_1, axis_linear, gc_block.values.n); + #else + mc_arc(gc_state.position, gc_block.values.xyz, gc_block.values.ijk, gc_block.values.r, + gc_state.feed_rate, gc_state.modal.feed_rate, axis_0, axis_1, axis_linear); + #endif + break; + case MOTION_MODE_PROBE: + #ifdef USE_LINE_NUMBERS + mc_probe_cycle(gc_block.values.xyz, gc_state.feed_rate, gc_state.modal.feed_rate, gc_block.values.n); + #else + mc_probe_cycle(gc_block.values.xyz, gc_state.feed_rate, gc_state.modal.feed_rate); + #endif + } + + // As far as the parser is concerned, the position is now == target. In reality the + // motion control system might still be processing the action and the real tool position + // in any intermediate location. + memcpy(gc_state.position, gc_block.values.xyz, sizeof(gc_block.values.xyz)); // gc.position[] = target[]; + } + } + + // [21. Program flow ]: // M0,M1,M2,M30: Perform non-running program flow actions. During a program pause, the buffer may // refill and can only be resumed by the cycle start run-time command. - if (gc.program_flow) { - plan_synchronize(); // Finish all remaining buffered motions. Program paused when complete. + gc_state.modal.program_flow = gc_block.modal.program_flow; + if (gc_state.modal.program_flow) { + protocol_buffer_synchronize(); // Finish all remaining buffered motions. Program paused when complete. sys.auto_start = false; // Disable auto cycle start. Forces pause until cycle start issued. - + // If complete, reset to reload defaults (G92.2,G54,G17,G90,G94,M48,G40,M5,M9). Otherwise, // re-enable program flow after pause complete, where cycle start will resume the program. - if (gc.program_flow == PROGRAM_FLOW_COMPLETED) { mc_reset(); } - else { gc.program_flow = PROGRAM_FLOW_RUNNING; } - } - - return(gc.status_code); -} - -// Parses the next statement and leaves the counter on the first character following -// the statement. Returns 1 if there was a statements, 0 if end of string was reached -// or there was an error (check state.status_code). -static int next_statement(char *letter, float *float_ptr, char *line, uint8_t *char_counter) -{ - if (line[*char_counter] == 0) { - return(0); // No more statements + if (gc_state.modal.program_flow == PROGRAM_FLOW_COMPLETED) { mc_reset(); } + else { gc_state.modal.program_flow = PROGRAM_FLOW_RUNNING; } } - - *letter = line[*char_counter]; - if((*letter < 'A') || (*letter > 'Z')) { - FAIL(STATUS_EXPECTED_COMMAND_LETTER); - return(0); - } - (*char_counter)++; - if (!read_float(line, char_counter, float_ptr)) { - FAIL(STATUS_BAD_NUMBER_FORMAT); - return(0); - }; - return(1); + + // TBD: % to denote start of program. Sets auto cycle start? + return(STATUS_OK); } + /* Not supported: @@ -587,17 +1004,19 @@ static int next_statement(char *letter, float *float_ptr, char *line, uint8_t *c - A,B,C-axes - Evaluation of expressions - Variables - - Probing - Override control (TBD) - Tool changes - Switches (*) Indicates optional parameter, enabled through config.h and re-compile group 0 = {G92.2, G92.3} (Non modal: Cancel and re-enable G92 offsets) - group 1 = {G38.2, G81 - G89} (Motion modes: straight probe, canned cycles) + group 1 = {G81 - G89} (Motion modes: Canned cycles) group 4 = {M1} (Optional stop, ignored) group 6 = {M6} (Tool change) + group 7 = {G40, G41, G42} cutter radius compensation + group 8 = {G43} tool length offset (But G43.1/G94 IS SUPPORTED) group 8 = {*M7} enable mist coolant group 9 = {M48, M49} enable/disable feed and speed override switches + group 10 = {G98, G99} return mode canned cycles group 13 = {G61, G61.1, G64} path control mode */ diff --git a/gcode.h b/gcode.h index 50b4228..b39e1f6 100644 --- a/gcode.h +++ b/gcode.h @@ -2,8 +2,8 @@ gcode.h - rs274/ngc parser. Part of Grbl + Copyright (c) 2011-2014 Sungeun K. Jeon Copyright (c) 2009-2011 Simen Svale Skogsrud - Copyright (c) 2011-2012 Sungeun K. Jeon Grbl is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -21,70 +21,166 @@ #ifndef gcode_h #define gcode_h -#include -#include "nuts_bolts.h" + +#include "system.h" // Define modal group internal numbers for checking multiple command violations and tracking the // type of command that is called in the block. A modal group is a group of g-code commands that are // mutually exclusive, or cannot exist on the same line, because they each toggle a state or execute // a unique motion. These are defined in the NIST RS274-NGC v3 g-code standard, available online, // and are similar/identical to other g-code interpreters by manufacturers (Haas,Fanuc,Mazak,etc). -#define MODAL_GROUP_NONE 0 -#define MODAL_GROUP_0 1 // [G4,G10,G28,G30,G53,G92,G92.1] Non-modal -#define MODAL_GROUP_1 2 // [G0,G1,G2,G3,G80] Motion -#define MODAL_GROUP_2 3 // [G17,G18,G19] Plane selection -#define MODAL_GROUP_3 4 // [G90,G91] Distance mode -#define MODAL_GROUP_4 5 // [M0,M1,M2,M30] Stopping -#define MODAL_GROUP_5 6 // [G93,G94] Feed rate mode -#define MODAL_GROUP_6 7 // [G20,G21] Units -#define MODAL_GROUP_7 8 // [M3,M4,M5] Spindle turning -#define MODAL_GROUP_12 9 // [G54,G55,G56,G57,G58,G59] Coordinate system selection +// NOTE: Modal group define values must be sequential and starting from zero. +#define MODAL_GROUP_G0 0 // [G4,G10,G28,G28.1,G30,G30.1,G53,G92,G92.1] Non-modal +#define MODAL_GROUP_G1 1 // [G0,G1,G2,G3,G38.2,G80] Motion +#define MODAL_GROUP_G2 2 // [G17,G18,G19] Plane selection +#define MODAL_GROUP_G3 3 // [G90,G91] Distance mode +#define MODAL_GROUP_G5 4 // [G93,G94] Feed rate mode +#define MODAL_GROUP_G6 5 // [G20,G21] Units +#define MODAL_GROUP_G8 6 // [G43,G43.1,G49] Tool length offset +#define MODAL_GROUP_G12 7 // [G54,G55,G56,G57,G58,G59] Coordinate system selection + +#define MODAL_GROUP_M4 8 // [M0,M1,M2,M30] Stopping +#define MODAL_GROUP_M7 9 // [M3,M4,M5] Spindle turning +#define MODAL_GROUP_M8 10 // [M7,M8,M9] Coolant control + +#define OTHER_INPUT_F 11 +#define OTHER_INPUT_S 12 +#define OTHER_INPUT_T 13 // Define command actions for within execution-type modal groups (motion, stopping, non-modal). Used // internally by the parser to know which command to execute. -#define MOTION_MODE_SEEK 0 // G0 -#define MOTION_MODE_LINEAR 1 // G1 -#define MOTION_MODE_CW_ARC 2 // G2 -#define MOTION_MODE_CCW_ARC 3 // G3 -#define MOTION_MODE_CANCEL 4 // G80 -#define PROGRAM_FLOW_RUNNING 0 -#define PROGRAM_FLOW_PAUSED 1 // M0, M1 -#define PROGRAM_FLOW_COMPLETED 2 // M2, M30 - -#define NON_MODAL_NONE 0 +// Modal Group G0: Non-modal actions +#define NON_MODAL_NO_ACTION 0 // (Default: Must be zero) #define NON_MODAL_DWELL 1 // G4 #define NON_MODAL_SET_COORDINATE_DATA 2 // G10 #define NON_MODAL_GO_HOME_0 3 // G28 #define NON_MODAL_SET_HOME_0 4 // G28.1 #define NON_MODAL_GO_HOME_1 5 // G30 #define NON_MODAL_SET_HOME_1 6 // G30.1 -#define NON_MODAL_SET_COORDINATE_OFFSET 7 // G92 -#define NON_MODAL_RESET_COORDINATE_OFFSET 8 //G92.1 +#define NON_MODAL_ABSOLUTE_OVERRIDE 7 // G53 +#define NON_MODAL_SET_COORDINATE_OFFSET 8 // G92 +#define NON_MODAL_RESET_COORDINATE_OFFSET 9 //G92.1 + +// Modal Group G1: Motion modes +#define MOTION_MODE_SEEK 0 // G0 (Default: Must be zero) +#define MOTION_MODE_LINEAR 1 // G1 +#define MOTION_MODE_CW_ARC 2 // G2 +#define MOTION_MODE_CCW_ARC 3 // G3 +#define MOTION_MODE_PROBE 4 // G38.2 +#define MOTION_MODE_NONE 5 // G80 + +// Modal Group G2: Plane select +#define PLANE_SELECT_XY 0 // G17 (Default: Must be zero) +#define PLANE_SELECT_ZX 1 // G18 +#define PLANE_SELECT_YZ 2 // G19 + +// Modal Group G3: Distance mode +#define DISTANCE_MODE_ABSOLUTE 0 // G90 (Default: Must be zero) +#define DISTANCE_MODE_INCREMENTAL 1 // G91 + +// Modal Group M4: Program flow +#define PROGRAM_FLOW_RUNNING 0 // (Default: Must be zero) +#define PROGRAM_FLOW_PAUSED 1 // M0, M1 +#define PROGRAM_FLOW_COMPLETED 2 // M2, M30 + +// Modal Group G5: Feed rate mode +#define FEED_RATE_MODE_UNITS_PER_MIN 0 // G94 (Default: Must be zero) +#define FEED_RATE_MODE_INVERSE_TIME 1 // G93 + +// Modal Group G6: Units mode +#define UNITS_MODE_MM 0 // G21 (Default: Must be zero) +#define UNITS_MODE_INCHES 1 // G20 + +// Modal Group M7: Spindle control +#define SPINDLE_DISABLE 0 // M5 (Default: Must be zero) +#define SPINDLE_ENABLE_CW 1 // M3 +#define SPINDLE_ENABLE_CCW 2 // M4 + +// Modal Group M8: Coolant control +#define COOLANT_DISABLE 0 // M9 (Default: Must be zero) +#define COOLANT_MIST_ENABLE 1 // M7 +#define COOLANT_FLOOD_ENABLE 2 // M8 + +// Modal Group G8: Tool length offset +#define TOOL_LENGTH_OFFSET_CANCEL 0 // G49 (Default: Must be zero) +#define TOOL_LENGTH_OFFSET_ENABLE_DYNAMIC 1 // G43.1 + +// Modal Group G12: Active work coordinate system +// N/A: Stores coordinate system value (54-59) to change to. + +#define WORD_F 0 +#define WORD_I 1 +#define WORD_J 2 +#define WORD_K 3 +#define WORD_L 4 +#define WORD_N 5 +#define WORD_P 6 +#define WORD_R 7 +#define WORD_S 8 +#define WORD_T 9 +#define WORD_X 10 +#define WORD_Y 11 +#define WORD_Z 12 + + + + +// NOTE: When this struct is zeroed, the above defines set the defaults for the system. +typedef struct { + uint8_t motion; // {G0,G1,G2,G3,G80} + uint8_t feed_rate; // {G93,G94} + uint8_t units; // {G20,G21} + uint8_t distance; // {G90,G91} + uint8_t plane_select; // {G17,G18,G19} + uint8_t tool_length; // {G43.1,G49} + uint8_t coord_select; // {G54,G55,G56,G57,G58,G59} + uint8_t program_flow; // {M0,M1,M2,M30} + uint8_t coolant; // {M7,M8,M9} + uint8_t spindle; // {M3,M4,M5} +} gc_modal_t; typedef struct { - uint8_t status_code; // Parser status for current block - uint8_t motion_mode; // {G0, G1, G2, G3, G80} - uint8_t inverse_feed_rate_mode; // {G93, G94} - uint8_t inches_mode; // 0 = millimeter mode, 1 = inches mode {G20, G21} - uint8_t absolute_mode; // 0 = relative motion, 1 = absolute motion {G90, G91} - uint8_t program_flow; // {M0, M1, M2, M30} - int8_t spindle_direction; // 1 = CW, -1 = CCW, 0 = Stop {M3, M4, M5} - uint8_t coolant_mode; // 0 = Disable, 1 = Flood Enable {M8, M9} - float feed_rate; // Millimeters/min - float position[N_AXIS]; // Where the interpreter considers the tool to be at this point in the code - uint8_t tool; -// uint16_t spindle_speed; // RPM/100 - uint8_t plane_axis_0, - plane_axis_1, - plane_axis_2; // The axes of the selected plane - uint8_t coord_select; // Active work coordinate system number. Default: 0=G54. - float coord_system[N_AXIS]; // Current work coordinate system (G54+). Stores offset from absolute machine - // position in mm. Loaded from EEPROM when called. - float coord_offset[N_AXIS]; // Retains the G92 coordinate offset (work coordinates) relative to - // machine zero in mm. Non-persistent. Cleared upon reset and boot. + float f; // Feed + float ijk[3]; // I,J,K Axis arc offsets + uint8_t l; // G10 or canned cycles parameters + int32_t n; // Line number + float p; // G10 or dwell parameters + // float q; // G82 peck drilling + float r; // Arc radius + float s; // Spindle speed + // uint8_t t; // Tool selection + float xyz[3]; // X,Y,Z Translational axes +} gc_values_t; + + +typedef struct { + gc_modal_t modal; + + float spindle_speed; // RPM + float feed_rate; // Millimeters/min + uint8_t tool; // Tracks tool number. NOT USED. + + float position[N_AXIS]; // Where the interpreter considers the tool to be at this point in the code + + float coord_system[N_AXIS]; // Current work coordinate system (G54+). Stores offset from absolute machine + // position in mm. Loaded from EEPROM when called. + float coord_offset[N_AXIS]; // Retains the G92 coordinate offset (work coordinates) relative to + // machine zero in mm. Non-persistent. Cleared upon reset and boot. + float tool_length_offset; // Tracks tool length offset value when enabled. } parser_state_t; -extern parser_state_t gc; +extern parser_state_t gc_state; + +typedef struct { +// uint16_t command_words; // NOTE: If this bitflag variable fills, G and M words can be separated. +// uint16_t value_words; + + uint8_t non_modal_command; + gc_modal_t modal; + gc_values_t values; + +} parser_block_t; +extern parser_block_t gc_block; // Initialize the parser void gc_init(); @@ -93,6 +189,6 @@ void gc_init(); uint8_t gc_execute_line(char *line); // Set g-code parser position. Input in steps. -void gc_set_current_position(int32_t x, int32_t y, int32_t z); +void gc_sync_position(); #endif diff --git a/limits.c b/limits.c index 3507785..030438e 100644 --- a/limits.c +++ b/limits.c @@ -2,8 +2,8 @@ limits.c - code pertaining to limit-switches and performing the homing cycle Part of Grbl + Copyright (c) 2012-2014 Sungeun K. Jeon Copyright (c) 2009-2011 Simen Svale Skogsrud - Copyright (c) 2012-2013 Sungeun K. Jeon Grbl is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -19,52 +19,65 @@ along with Grbl. If not, see . */ -#include -#include -#include -#include "stepper.h" +#include "system.h" #include "settings.h" -#include "nuts_bolts.h" -#include "config.h" -#include "spindle_control.h" -#include "motion_control.h" -#include "planner.h" #include "protocol.h" +#include "planner.h" +#include "stepper.h" +#include "motion_control.h" #include "limits.h" #include "report.h" -#define MICROSECONDS_PER_ACCELERATION_TICK (1000000/ACCELERATION_TICKS_PER_SECOND) + +#define HOMING_AXIS_SEARCH_SCALAR 1.5 // Axis search distance multiplier. Must be > 1. + void limits_init() { LIMIT_DDR &= ~(LIMIT_MASK); // Set as input pins - LIMIT_PORT |= (LIMIT_MASK); // Enable internal pull-up resistors. Normal high operation. + + if (bit_istrue(settings.flags,BITFLAG_INVERT_LIMIT_PINS)) { + LIMIT_PORT &= ~(LIMIT_MASK); // Normal low operation. Requires external pull-down. + } else { + LIMIT_PORT |= (LIMIT_MASK); // Enable internal pull-up resistors. Normal high operation. + } + if (bit_istrue(settings.flags,BITFLAG_HARD_LIMIT_ENABLE)) { LIMIT_PCMSK |= LIMIT_MASK; // Enable specific pins of the Pin Change Interrupt PCICR |= (1 << LIMIT_INT); // Enable Pin Change Interrupt } else { - LIMIT_PCMSK &= ~LIMIT_MASK; // Disable - PCICR &= ~(1 << LIMIT_INT); + limits_disable(); } + + #ifdef ENABLE_SOFTWARE_DEBOUNCE + MCUSR &= ~(1< dt_min) { dt = dt_min; } // Disable acceleration for very slow rates. - - // Set default out_bits. - uint8_t out_bits0 = settings.invert_mask; - out_bits0 ^= (settings.homing_dir_mask & DIRECTION_MASK); // Apply homing direction settings - if (!pos_dir) { out_bits0 ^= DIRECTION_MASK; } // Invert bits, if negative dir. - - // Initialize stepping variables - int32_t counter_x = -(step_event_count >> 1); // Bresenham counters - int32_t counter_y = counter_x; - int32_t counter_z = counter_x; - uint32_t step_delay = dt-settings.pulse_microseconds; // Step delay after pulse - uint32_t step_rate = 0; // Tracks step rate. Initialized from 0 rate. (in step/min) - uint32_t trap_counter = MICROSECONDS_PER_ACCELERATION_TICK/2; // Acceleration trapezoid counter - uint8_t out_bits; - uint8_t limit_state; - for(;;) { - - // Reset out bits. Both direction and step pins appropriately inverted and set. - out_bits = out_bits0; - - // Get limit pin state. - limit_state = LIMIT_PIN; - if (invert_pin) { limit_state ^= LIMIT_MASK; } // If leaving switch, invert to move. - - // Set step pins by Bresenham line algorithm. If limit switch reached, disable and - // flag for completion. - if (cycle_mask & (1< 0) { - if (limit_state & (1< 0) { - if (limit_state & (1< 0) { - if (limit_state & (1< dt_min) { // Unless cruising, check for time update. - trap_counter += dt; // Track time passed since last update. - if (trap_counter > MICROSECONDS_PER_ACCELERATION_TICK) { - trap_counter -= MICROSECONDS_PER_ACCELERATION_TICK; - step_rate += delta_rate; // Increment velocity - dt = (1000000*60)/step_rate; // Compute new time increment - if (dt < dt_min) {dt = dt_min;} // If target rate reached, cruise. - step_delay = dt-settings.pulse_microseconds; - } - } + } } } +#endif -void limits_go_home() -{ - // Enable only the steppers, not the cycle. Cycle should be inactive/complete. - st_wake_up(); +// Homes the specified cycle axes, sets the machine position, and performs a pull-off motion after +// completing. Homing is a special motion case, which involves rapid uncontrolled stops to locate +// the trigger point of the limit switches. The rapid stops are handled by a system level axis lock +// mask, which prevents the stepper algorithm from executing step pulses. Homing motions typically +// circumvent the processes for executing motions in normal operation. +// NOTE: Only the abort runtime command can interrupt this process. +void limits_go_home(uint8_t cycle_mask) +{ + if (sys.abort) { return; } // Block if system reset has been issued. + + // Initialize homing in search mode to quickly engage the specified cycle_mask limit switches. + bool approach = true; + float homing_rate = settings.homing_seek_rate; + uint8_t invert_pin, idx; + uint8_t n_cycle = (2*N_HOMING_LOCATE_CYCLE+1); + float target[N_AXIS]; - // Search to engage all axes limit switches at faster homing seek rate. - homing_cycle(HOMING_SEARCH_CYCLE_0, true, false, settings.homing_seek_rate); // Search cycle 0 - #ifdef HOMING_SEARCH_CYCLE_1 - homing_cycle(HOMING_SEARCH_CYCLE_1, true, false, settings.homing_seek_rate); // Search cycle 1 - #endif - #ifdef HOMING_SEARCH_CYCLE_2 - homing_cycle(HOMING_SEARCH_CYCLE_2, true, false, settings.homing_seek_rate); // Search cycle 2 - #endif - delay_ms(settings.homing_debounce_delay); // Delay to debounce signal + // Determine travel distance to the furthest homing switch based on user max travel settings. + // NOTE: settings.max_travel[] is stored as a negative value. + float max_travel = settings.max_travel[X_AXIS]; + if (max_travel > settings.max_travel[Y_AXIS]) { max_travel = settings.max_travel[Y_AXIS]; } + if (max_travel > settings.max_travel[Z_AXIS]) { max_travel = settings.max_travel[Z_AXIS]; } + max_travel *= -HOMING_AXIS_SEARCH_SCALAR; // Ensure homing switches engaged by over-estimating max travel. + + plan_reset(); // Reset planner buffer to zero planner current position and to clear previous motions. + + do { + // Initialize invert_pin boolean based on approach and invert pin user setting. + if (bit_isfalse(settings.flags,BITFLAG_INVERT_LIMIT_PINS)) { invert_pin = approach; } + else { invert_pin = !approach; } + + // Set target location and rate for active axes. + uint8_t n_active_axis = 0; + for (idx=0; idx 0) { - // Re-approach all switches to re-engage them. - homing_cycle(HOMING_LOCATE_CYCLE, true, false, settings.homing_feed_rate); - delay_ms(settings.homing_debounce_delay); + } while (n_cycle-- > 0); + + // The active cycle axes should now be homed and machine limits have been located. By + // default, grbl defines machine space as all negative, as do most CNCs. Since limit switches + // can be on either side of an axes, check and set axes machine zero appropriately. Also, + // set up pull-off maneuver from axes limit switches that have been homed. This provides + // some initial clearance off the switches and should also help prevent them from falsely + // triggering when hard limits are enabled or when more than one axes shares a limit pin. + for (idx=0; idx 0 || target[X_AXIS] < -settings.max_travel[X_AXIS] || - target[Y_AXIS] > 0 || target[Y_AXIS] < -settings.max_travel[Y_AXIS] || - target[Z_AXIS] > 0 || target[Z_AXIS] < -settings.max_travel[Z_AXIS] ) { - - // Force feed hold if cycle is active. All buffered blocks are guaranteed to be within - // workspace volume so just come to a controlled stop so position is not lost. When complete - // enter alarm mode. - if (sys.state == STATE_CYCLE) { - st_feed_hold(); - while (sys.state == STATE_HOLD) { - protocol_execute_runtime(); - if (sys.abort) { return; } - } - } + uint8_t idx; + for (idx=0; idx 0 || target[idx] < settings.max_travel[idx]) { // NOTE: max_travel is stored as negative - mc_reset(); // Issue system reset and ensure spindle and coolant are shutdown. - sys.execute |= EXEC_CRIT_EVENT; // Indicate soft limit critical event - protocol_execute_runtime(); // Execute to enter critical event loop and system abort + // Force feed hold if cycle is active. All buffered blocks are guaranteed to be within + // workspace volume so just come to a controlled stop so position is not lost. When complete + // enter alarm mode. + if (sys.state == STATE_CYCLE) { + bit_true_atomic(sys.execute, EXEC_FEED_HOLD); + do { + protocol_execute_runtime(); + if (sys.abort) { return; } + } while ( sys.state != STATE_IDLE || sys.state != STATE_QUEUED); + } + + mc_reset(); // Issue system reset and ensure spindle and coolant are shutdown. + bit_true_atomic(sys.execute, (EXEC_ALARM | EXEC_CRIT_EVENT)); // Indicate soft limit critical event + protocol_execute_runtime(); // Execute to enter critical event loop and system abort + return; + + } } } diff --git a/limits.h b/limits.h index ac94dd6..4f31fc4 100644 --- a/limits.h +++ b/limits.h @@ -2,8 +2,8 @@ limits.h - code pertaining to limit-switches and performing the homing cycle Part of Grbl + Copyright (c) 2013-2014 Sungeun K. Jeon Copyright (c) 2009-2011 Simen Svale Skogsrud - Copyright (c) 2013 Sungeun K. Jeon Grbl is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -22,11 +22,14 @@ #ifndef limits_h #define limits_h + // Initialize the limits module void limits_init(); -// Perform the homing cycle -void limits_go_home(); +void limits_disable(); + +// Perform one portion of the homing cycle based on the input settings. +void limits_go_home(uint8_t cycle_mask); // Check for soft limit violations void limits_soft_check(float *target); diff --git a/main.c b/main.c index 84b0313..c981cb1 100644 --- a/main.c +++ b/main.c @@ -1,9 +1,9 @@ /* main.c - An embedded CNC Controller with rs274/ngc (g-code) support Part of Grbl - + + Copyright (c) 2011-2014 Sungeun K. Jeon Copyright (c) 2009-2011 Simen Svale Skogsrud - Copyright (c) 2011-2013 Sungeun K. Jeon Grbl is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -19,95 +19,78 @@ along with Grbl. If not, see . */ -/* A big thanks to Alden Hart of Synthetos, supplier of grblshield and TinyG, who has - been integral throughout the development of the higher level details of Grbl, as well - as being a consistent sounding board for the future of accessible and free CNC. */ - -#include -#include -#include "config.h" +#include "system.h" +#include "serial.h" +#include "settings.h" +#include "protocol.h" +#include "gcode.h" #include "planner.h" -#include "nuts_bolts.h" #include "stepper.h" #include "spindle_control.h" #include "coolant_control.h" #include "motion_control.h" -#include "gcode.h" -#include "protocol.h" #include "limits.h" +#include "probe.h" #include "report.h" -#include "settings.h" -#include "serial.h" + // Declare system global variable structure system_t sys; + int main(void) { - // Initialize system - serial_init(); // Setup serial baud rate and interrupts + // Initialize system upon power-up. + serial_init(); // Setup serial baud rate and interrupts settings_init(); // Load grbl settings from EEPROM - st_init(); // Setup stepper pins and interrupt timers - sei(); // Enable interrupts + stepper_init(); // Configure stepper pins and interrupt timers + system_init(); // Configure pinout pins and pin-change interrupt memset(&sys, 0, sizeof(sys)); // Clear all system variables sys.abort = true; // Set abort to complete initialization - sys.state = STATE_INIT; // Set alarm state to indicate unknown initial position + sei(); // Enable interrupts + + // Check for power-up and set system alarm if homing is enabled to force homing cycle + // by setting Grbl's alarm state. Alarm locks out all g-code commands, including the + // startup scripts, but allows access to settings and internal commands. Only a homing + // cycle '$H' or kill alarm locks '$X' will disable the alarm. + // NOTE: The startup script will run after successful completion of the homing cycle, but + // not after disabling the alarm locks. Prevents motion startup blocks from crashing into + // things uncontrollably. Very bad. + #ifdef HOMING_INIT_LOCK + if (bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) { sys.state = STATE_ALARM; } + #endif + // Grbl initialization loop upon power-up or a system abort. For the latter, all processes + // will return to this loop to be cleanly re-initialized. for(;;) { + + // TODO: Separate configure task that require interrupts to be disabled, especially upon + // a system abort and ensuring any active interrupts are cleanly reset. - // Execute system reset upon a system abort, where the main program will return to this loop. - // Once here, it is safe to re-initialize the system. At startup, the system will automatically - // reset to finish the initialization process. - if (sys.abort) { - // Reset system. - serial_reset_read_buffer(); // Clear serial read buffer - plan_init(); // Clear block buffer and planner variables - gc_init(); // Set g-code parser to default state - protocol_init(); // Clear incoming line data and execute startup lines - spindle_init(); - coolant_init(); - limits_init(); - st_reset(); // Clear stepper subsystem variables. + // Reset Grbl primary systems. + serial_reset_read_buffer(); // Clear serial read buffer + gc_init(); // Set g-code parser to default state + spindle_init(); + coolant_init(); + limits_init(); + probe_init(); + plan_reset(); // Clear block buffer and planner variables + st_reset(); // Clear stepper subsystem variables. - // Sync cleared gcode and planner positions to current system position, which is only - // cleared upon startup, not a reset/abort. - sys_sync_current_position(); + // Sync cleared gcode and planner positions to current system position. + plan_sync_position(); + gc_sync_position(); - // Reset system variables. - sys.abort = false; - sys.execute = 0; - if (bit_istrue(settings.flags,BITFLAG_AUTO_START)) { sys.auto_start = true; } - - // Check for power-up and set system alarm if homing is enabled to force homing cycle - // by setting Grbl's alarm state. Alarm locks out all g-code commands, including the - // startup scripts, but allows access to settings and internal commands. Only a homing - // cycle '$H' or kill alarm locks '$X' will disable the alarm. - // NOTE: The startup script will run after successful completion of the homing cycle, but - // not after disabling the alarm locks. Prevents motion startup blocks from crashing into - // things uncontrollably. Very bad. - #ifdef HOMING_INIT_LOCK - if (sys.state == STATE_INIT && bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) { sys.state = STATE_ALARM; } - #endif - - // Check for and report alarm state after a reset, error, or an initial power up. - if (sys.state == STATE_ALARM) { - report_feedback_message(MESSAGE_ALARM_LOCK); - } else { - // All systems go. Set system to ready and execute startup script. - sys.state = STATE_IDLE; - protocol_execute_startup(); - } - } - - protocol_execute_runtime(); - protocol_process(); // ... process the serial protocol - - // When the serial protocol returns, there are no more characters in the serial read buffer to - // be processed and executed. This indicates that individual commands are being issued or - // streaming is finished. In either case, auto-cycle start, if enabled, any queued moves. - mc_auto_cycle_start(); + // Reset system variables. + sys.abort = false; + sys.execute = 0; + if (bit_istrue(settings.flags,BITFLAG_AUTO_START)) { sys.auto_start = true; } + else { sys.auto_start = false; } + + // Start Grbl main loop. Processes program inputs and executes them. + protocol_main_loop(); } - return 0; /* never reached */ + return 0; /* Never reached */ } diff --git a/motion_control.c b/motion_control.c index 2a2f508..f3c8b2d 100644 --- a/motion_control.c +++ b/motion_control.c @@ -2,8 +2,8 @@ motion_control.c - high level interface for issuing motion commands Part of Grbl + Copyright (c) 2011-2014 Sungeun K. Jeon Copyright (c) 2009-2011 Simen Svale Skogsrud - Copyright (c) 2011-2013 Sungeun K. Jeon Copyright (c) 2011 Jens Geisler Grbl is free software: you can redistribute it and/or modify @@ -20,21 +20,19 @@ along with Grbl. If not, see . */ -#include -#include -#include -#include +#include "system.h" #include "settings.h" -#include "config.h" +#include "protocol.h" #include "gcode.h" +#include "planner.h" +#include "stepper.h" #include "motion_control.h" #include "spindle_control.h" #include "coolant_control.h" -#include "nuts_bolts.h" -#include "stepper.h" -#include "planner.h" #include "limits.h" -#include "protocol.h" +#include "probe.h" +#include "report.h" + // Execute linear motion in absolute millimeter coordinates. Feed rate given in millimeters/second // unless invert_feed_rate is true. Then the feed_rate means that the motion should be completed in @@ -43,7 +41,11 @@ // segments, must pass through this routine before being passed to the planner. The seperation of // mc_line and plan_buffer_line is done primarily to place non-planner-type functions from being // in the planner and to let backlash compensation or canned cycle integration simple and direct. +#ifdef USE_LINE_NUMBERS +void mc_line(float *target, float feed_rate, uint8_t invert_feed_rate, int32_t line_number) +#else void mc_line(float *target, float feed_rate, uint8_t invert_feed_rate) +#endif { // If enabled, check for soft limit violations. Placed here all line motions are picked up // from everywhere in Grbl. @@ -52,28 +54,35 @@ void mc_line(float *target, float feed_rate, uint8_t invert_feed_rate) // If in check gcode mode, prevent motion by blocking planner. Soft limits still work. if (sys.state == STATE_CHECK_MODE) { return; } - // TODO: Backlash compensation may be installed here. Only need direction info to track when - // to insert a backlash line motion(s) before the intended line motion. Requires its own + // NOTE: Backlash compensation may be installed here. It will need direction info to track when + // to insert a backlash line motion(s) before the intended line motion and will require its own // plan_check_full_buffer() and check for system abort loop. Also for position reporting - // backlash steps will need to be also tracked. Not sure what the best strategy is for this, - // i.e. keep the planner independent and do the computations in the status reporting, or let - // the planner handle the position corrections. The latter may get complicated. - // TODO: Backlash comp positioning values may need to be kept at a system level, i.e. tracking - // true position after a feed hold in the middle of a backlash move. The difficulty is in making - // sure that the stepper subsystem and planner are working in sync, and the status report - // position also takes this into account. + // backlash steps will need to be also tracked, which will need to be kept at a system level. + // There are likely some other things that will need to be tracked as well. However, we feel + // that backlash compensation should NOT be handled by Grbl itself, because there are a myriad + // of ways to implement it and can be effective or ineffective for different CNC machines. This + // would be better handled by the interface as a post-processor task, where the original g-code + // is translated and inserts backlash motions that best suits the machine. + // NOTE: Perhaps as a middle-ground, all that needs to be sent is a flag or special command that + // indicates to Grbl what is a backlash compensation motion, so that Grbl executes the move but + // doesn't update the machine position values. Since the position values used by the g-code + // parser and planner are separate from the system machine positions, this is doable. // If the buffer is full: good! That means we are well ahead of the robot. // Remain in this loop until there is room in the buffer. do { protocol_execute_runtime(); // Check for any run-time commands if (sys.abort) { return; } // Bail, if system abort. - if ( plan_check_full_buffer() ) { mc_auto_cycle_start(); } // Auto-cycle start when buffer is full. + if ( plan_check_full_buffer() ) { protocol_auto_cycle_start(); } // Auto-cycle start when buffer is full. else { break; } } while (1); - plan_buffer_line(target[X_AXIS], target[Y_AXIS], target[Z_AXIS], feed_rate, invert_feed_rate); - + #ifdef USE_LINE_NUMBERS + plan_buffer_line(target, feed_rate, invert_feed_rate, line_number); + #else + plan_buffer_line(target, feed_rate, invert_feed_rate); + #endif + // If idle, indicate to the system there is now a planned block in the buffer ready to cycle // start. Otherwise ignore and continue on. if (!sys.state) { sys.state = STATE_QUEUED; } @@ -87,12 +96,16 @@ void mc_line(float *target, float feed_rate, uint8_t invert_feed_rate) // The arc is approximated by generating a huge number of tiny, linear segments. The chordal tolerance // of each segment is configured in settings.arc_tolerance, which is defined to be the maximum normal // distance from segment to the circle when the end points both lie on the circle. -void mc_arc(float *position, float *target, float *offset, uint8_t axis_0, uint8_t axis_1, - uint8_t axis_linear, float feed_rate, uint8_t invert_feed_rate, float radius, uint8_t isclockwise) -{ +#ifdef USE_LINE_NUMBERS +void mc_arc(float *position, float *target, float *offset, float radius, float feed_rate, + uint8_t invert_feed_rate, uint8_t axis_0, uint8_t axis_1, uint8_t axis_linear, int32_t line_number) +#else +void mc_arc(float *position, float *target, float *offset, float radius, float feed_rate, + uint8_t invert_feed_rate, uint8_t axis_0, uint8_t axis_1, uint8_t axis_linear) +#endif +{ float center_axis0 = position[axis_0] + offset[axis_0]; float center_axis1 = position[axis_1] + offset[axis_1]; - float linear_travel = target[axis_linear] - position[axis_linear]; float r_axis0 = -offset[axis_0]; // Radius vector from center to current location float r_axis1 = -offset[axis_1]; float rt_axis0 = target[axis_0] - center_axis0; @@ -100,7 +113,7 @@ void mc_arc(float *position, float *target, float *offset, uint8_t axis_0, uint8 // CCW angle between position and target from circle center. Only one atan2() trig computation required. float angular_travel = atan2(r_axis0*rt_axis1-r_axis1*rt_axis0, r_axis0*rt_axis0+r_axis1*rt_axis1); - if (isclockwise) { // Correct atan2 output per direction + if (gc_state.modal.motion == MOTION_MODE_CW_ARC) { // Correct atan2 output per direction if (angular_travel >= 0) { angular_travel -= 2*M_PI; } } else { if (angular_travel <= 0) { angular_travel += 2*M_PI; } @@ -109,11 +122,9 @@ void mc_arc(float *position, float *target, float *offset, uint8_t axis_0, uint8 // NOTE: Segment end points are on the arc, which can lead to the arc diameter being smaller by up to // (2x) settings.arc_tolerance. For 99% of users, this is just fine. If a different arc segment fit // is desired, i.e. least-squares, midpoint on arc, just change the mm_per_arc_segment calculation. - // Computes: mm_per_arc_segment = sqrt(4*arc_tolerance*(2*radius-arc_tolerance)), - // segments = millimeters_of_travel/mm_per_arc_segment - float millimeters_of_travel = hypot(angular_travel*radius, fabs(linear_travel)); - uint16_t segments = floor(millimeters_of_travel/ - sqrt(4*settings.arc_tolerance*(2*radius - settings.arc_tolerance)) ); + // For the intended uses of Grbl, this value shouldn't exceed 2000 for the strictest of cases. + uint16_t segments = floor(fabs(0.5*angular_travel*radius)/ + sqrt(settings.arc_tolerance*(2*radius - settings.arc_tolerance)) ); if (segments) { // Multiply inverse feed_rate to compensate for the fact that this movement is approximated @@ -122,8 +133,8 @@ void mc_arc(float *position, float *target, float *offset, uint8_t axis_0, uint8 if (invert_feed_rate) { feed_rate *= segments; } float theta_per_segment = angular_travel/segments; - float linear_per_segment = linear_travel/segments; - + float linear_per_segment = (target[axis_linear] - position[axis_linear])/segments; + /* Vector rotation by transformation matrix: r is the original vector, r_T is the rotated vector, and phi is the angle of rotation. Solution approach by Jens Geisler. r_T = [cos(phi) -sin(phi); @@ -148,21 +159,17 @@ void mc_arc(float *position, float *target, float *offset, uint8_t axis_0, uint8 This is important when there are successive arc motions. */ // Computes: cos_T = 1 - theta_per_segment^2/2, sin_T = theta_per_segment - theta_per_segment^3/6) in ~52usec - float cos_T = 2 - theta_per_segment*theta_per_segment; - float sin_T = theta_per_segment*0.16666667*(cos_T + 4); + float cos_T = 2.0 - theta_per_segment*theta_per_segment; + float sin_T = theta_per_segment*0.16666667*(cos_T + 4.0); cos_T *= 0.5; - float arc_target[N_AXIS]; float sin_Ti; float cos_Ti; float r_axisi; uint16_t i; uint8_t count = 0; - // Initialize the linear axis - arc_target[axis_linear] = position[axis_linear]; - - for (i = 1; i 0) { // NOTE: Check and execute runtime commands during dwell every <= DWELL_TIME_STEP milliseconds. protocol_execute_runtime(); @@ -213,69 +231,101 @@ void mc_dwell(float seconds) // Perform homing cycle to locate and set machine zero. Only '$H' executes this command. // NOTE: There should be no motions in the buffer and Grbl must be in an idle state before // executing the homing cycle. This prevents incorrect buffered plans after homing. -void mc_go_home() +void mc_homing_cycle() { sys.state = STATE_HOMING; // Set system state variable - LIMIT_PCMSK &= ~LIMIT_MASK; // Disable hard limits pin change register for cycle duration - - limits_go_home(); // Perform homing routine. + limits_disable(); // Disable hard limits pin change register for cycle duration + + // ------------------------------------------------------------------------------------- + // Perform homing routine. NOTE: Special motion case. Only system reset works. + // Search to engage all axes limit switches at faster homing seek rate. + limits_go_home(HOMING_CYCLE_0); // Homing cycle 0 + #ifdef HOMING_CYCLE_1 + limits_go_home(HOMING_CYCLE_1); // Homing cycle 1 + #endif + #ifdef HOMING_CYCLE_2 + limits_go_home(HOMING_CYCLE_2); // Homing cycle 2 + #endif + protocol_execute_runtime(); // Check for reset and set system abort. if (sys.abort) { return; } // Did not complete. Alarm state set by mc_alarm. - // The machine should now be homed and machine limits have been located. By default, - // grbl defines machine space as all negative, as do most CNCs. Since limit switches - // can be on either side of an axes, check and set machine zero appropriately. - // At the same time, set up pull-off maneuver from axes limit switches that have been homed. - // This provides some initial clearance off the switches and should also help prevent them - // from falsely tripping when hard limits are enabled. - // TODO: Need to improve dir_mask[] to be more axes independent. - float pulloff_target[N_AXIS]; - clear_vector_float(pulloff_target); // Zero pulloff target. - clear_vector_long(sys.position); // Zero current position for now. - uint8_t dir_mask[N_AXIS]; - dir_mask[X_AXIS] = (1< -#include "planner.h" +#define HOMING_CYCLE_LINE_NUMBER -1 // Execute linear motion in absolute millimeter coordinates. Feed rate given in millimeters/second // unless invert_feed_rate is true. Then the feed_rate means that the motion should be completed in // (1 minute)/feed_rate time. +#ifdef USE_LINE_NUMBERS +void mc_line(float *target, float feed_rate, uint8_t invert_feed_rate, int32_t line_number); +#else void mc_line(float *target, float feed_rate, uint8_t invert_feed_rate); +#endif // Execute an arc in offset mode format. position == current xyz, target == target xyz, // offset == offset from current xyz, axis_XXX defines circle plane in tool space, axis_linear is // the direction of helical travel, radius == circle radius, isclockwise boolean. Used // for vector transformation direction. -void mc_arc(float *position, float *target, float *offset, uint8_t axis_0, uint8_t axis_1, - uint8_t axis_linear, float feed_rate, uint8_t invert_feed_rate, float radius, uint8_t isclockwise); +#ifdef USE_LINE_NUMBERS +void mc_arc(float *position, float *target, float *offset, float radius, float feed_rate, + uint8_t invert_feed_rate, uint8_t axis_0, uint8_t axis_1, uint8_t axis_linear, int32_t line_number); +#else +void mc_arc(float *position, float *target, float *offset, float radius, float feed_rate, + uint8_t invert_feed_rate, uint8_t axis_0, uint8_t axis_1, uint8_t axis_linear); +#endif // Dwell for a specific number of seconds void mc_dwell(float seconds); // Perform homing cycle to locate machine zero. Requires limit switches. -void mc_go_home(); +void mc_homing_cycle(); + +// Perform tool length probe cycle. Requires probe switch. +#ifdef USE_LINE_NUMBERS +void mc_probe_cycle(float *target, float feed_rate, uint8_t invert_feed_rate, int32_t line_number); +#else +void mc_probe_cycle(float *target, float feed_rate, uint8_t invert_feed_rate); +#endif // Performs system reset. If in motion state, kills all motion and sets system alarm. void mc_reset(); -// Executes the auto cycle feature, if enabled. -void mc_auto_cycle_start(); - #endif diff --git a/nuts_bolts.c b/nuts_bolts.c index a244bfb..a8a159c 100644 --- a/nuts_bolts.c +++ b/nuts_bolts.c @@ -2,8 +2,8 @@ nuts_bolts.c - Shared functions Part of Grbl + Copyright (c) 2011-2014 Sungeun K. Jeon Copyright (c) 2009-2011 Simen Svale Skogsrud - Copyright (c) 2011-2012 Sungeun K. Jeon Grbl is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -19,13 +19,11 @@ along with Grbl. If not, see . */ -#include -#include "nuts_bolts.h" -#include "gcode.h" -#include "planner.h" +#include "system.h" + #define MAX_INT_DIGITS 8 // Maximum number of digits in int32 (and float) -extern float __floatunsisf (unsigned long); + // Extracts a floating point value from a string. The following code is based loosely on // the avr-libc strtod() function by Michael Stumpf and Dmitry Xmelkov and many freely @@ -34,7 +32,7 @@ extern float __floatunsisf (unsigned long); // Scientific notation is officially not supported by g-code, and the 'E' character may // be a g-code word on some CNC systems. So, 'E' notation will not be recognized. // NOTE: Thanks to Radu-Eosif Mihailescu for identifying the issues with using strtod(). -int read_float(char *line, uint8_t *char_counter, float *float_ptr) +uint8_t read_float(char *line, uint8_t *char_counter, float *float_ptr) { char *ptr = line + *char_counter; unsigned char c; @@ -79,7 +77,7 @@ int read_float(char *line, uint8_t *char_counter, float *float_ptr) // Convert integer into floating point. float fval; - fval = __floatunsisf(intval); + fval = (float)intval; // Apply decimal. Should perform no more than two floating point multiplications for the // expected range of E0 to E-4. @@ -140,9 +138,22 @@ void delay_us(uint32_t us) } } -// Syncs all internal position vectors to the current system position. -void sys_sync_current_position() + +// Returns direction mask according to Grbl internal axis indexing. +uint8_t get_direction_mask(uint8_t axis_idx) { - plan_set_current_position(sys.position[X_AXIS],sys.position[Y_AXIS],sys.position[Z_AXIS]); - gc_set_current_position(sys.position[X_AXIS],sys.position[Y_AXIS],sys.position[Z_AXIS]); + uint8_t axis_mask = 0; + switch( axis_idx ) { + case X_AXIS: axis_mask = (1< -#include -#include -#include "config.h" -#include "defaults.h" - #define false 0 #define true 1 @@ -44,60 +38,24 @@ // Useful macros #define clear_vector(a) memset(a, 0, sizeof(a)) #define clear_vector_float(a) memset(a, 0.0, sizeof(float)*N_AXIS) -#define clear_vector_long(a) memset(a, 0.0, sizeof(long)*N_AXIS) +// #define clear_vector_long(a) memset(a, 0.0, sizeof(long)*N_AXIS) #define max(a,b) (((a) > (b)) ? (a) : (b)) #define min(a,b) (((a) < (b)) ? (a) : (b)) // Bit field and masking macros #define bit(n) (1 << n) -#define bit_true(x,mask) (x |= mask) -#define bit_false(x,mask) (x &= ~mask) -#define bit_toggle(x,mask) (x ^= mask) +#define bit_true_atomic(x,mask) {uint8_t sreg = SREG; cli(); (x) |= (mask); SREG = sreg; } +#define bit_false_atomic(x,mask) {uint8_t sreg = SREG; cli(); (x) &= ~(mask); SREG = sreg; } +#define bit_toggle_atomic(x,mask) {uint8_t sreg = SREG; cli(); (x) ^= (mask); SREG = sreg; } +#define bit_true(x,mask) (x) |= (mask) +#define bit_false(x,mask) (x) &= ~(mask) #define bit_istrue(x,mask) ((x & mask) != 0) #define bit_isfalse(x,mask) ((x & mask) == 0) -// Define system executor bit map. Used internally by runtime protocol as runtime command flags, -// which notifies the main program to execute the specified runtime command asynchronously. -// NOTE: The system executor uses an unsigned 8-bit volatile variable (8 flag limit.) The default -// flags are always false, so the runtime protocol only needs to check for a non-zero value to -// know when there is a runtime command to execute. -#define EXEC_STATUS_REPORT bit(0) // bitmask 00000001 -#define EXEC_CYCLE_START bit(1) // bitmask 00000010 -#define EXEC_CYCLE_STOP bit(2) // bitmask 00000100 -#define EXEC_FEED_HOLD bit(3) // bitmask 00001000 -#define EXEC_RESET bit(4) // bitmask 00010000 -#define EXEC_ALARM bit(5) // bitmask 00100000 -#define EXEC_CRIT_EVENT bit(6) // bitmask 01000000 -// #define bit(7) // bitmask 10000000 - -// Define system state bit map. The state variable primarily tracks the individual functions -// of Grbl to manage each without overlapping. It is also used as a messaging flag for -// critical events. -#define STATE_IDLE 0 // Must be zero. -#define STATE_INIT 1 // Initial power up state. -#define STATE_QUEUED 2 // Indicates buffered blocks, awaiting cycle start. -#define STATE_CYCLE 3 // Cycle is running -#define STATE_HOLD 4 // Executing feed hold -#define STATE_HOMING 5 // Performing homing cycle -#define STATE_ALARM 6 // In alarm state. Locks out all g-code processes. Allows settings access. -#define STATE_CHECK_MODE 7 // G-code check mode. Locks out planner and motion only. -// #define STATE_JOG 8 // Jogging mode is unique like homing. - -// Define global system variables -typedef struct { - uint8_t abort; // System abort flag. Forces exit back to main loop for reset. - uint8_t state; // Tracks the current state of Grbl. - volatile uint8_t execute; // Global system runtime executor bitflag variable. See EXEC bitmasks. - int32_t position[N_AXIS]; // Real-time machine (aka home) position vector in steps. - // NOTE: This may need to be a volatile variable, if problems arise. - uint8_t auto_start; // Planner auto-start flag. Toggled off during feed hold. Defaulted by settings. -} system_t; -extern system_t sys; - // Read a floating point value from a string. Line points to the input buffer, char_counter // is the indexer pointing to the current character of the line, while float_ptr is // a pointer to the result variable. Returns true when it succeeds -int read_float(char *line, uint8_t *char_counter, float *float_ptr); +uint8_t read_float(char *line, uint8_t *char_counter, float *float_ptr); // Delays variable-defined milliseconds. Compiler compatibility fix for _delay_ms(). void delay_ms(uint16_t ms); @@ -105,7 +63,8 @@ void delay_ms(uint16_t ms); // Delays variable-defined microseconds. Compiler compatibility fix for _delay_us(). void delay_us(uint32_t us); -// Syncs Grbl's gcode and planner position variables with the system position. -void sys_sync_current_position(); +uint8_t get_direction_mask(uint8_t i); + +float hypot_f(float x, float y); #endif diff --git a/planner.c b/planner.c index c1a4d4f..7ae9fce 100644 --- a/planner.c +++ b/planner.c @@ -2,8 +2,8 @@ planner.c - buffers movement commands and manages the acceleration profile plan Part of Grbl + Copyright (c) 2011-2014 Sungeun K. Jeon Copyright (c) 2009-2011 Simen Svale Skogsrud - Copyright (c) 2011-2013 Sungeun K. Jeon Copyright (c) 2011 Jens Geisler Grbl is free software: you can redistribute it and/or modify @@ -22,23 +22,21 @@ /* The ring buffer implementation gleaned from the wiring_serial library by David A. Mellis. */ -#include -#include +#include "system.h" #include "planner.h" -#include "nuts_bolts.h" +#include "protocol.h" #include "stepper.h" #include "settings.h" -#include "config.h" -#include "protocol.h" + #define SOME_LARGE_VALUE 1.0E+38 // Used by rapids and acceleration maximization calculations. Just needs // to be larger than any feasible (mm/min)^2 or mm/sec^2 value. -static block_t block_buffer[BLOCK_BUFFER_SIZE]; // A ring buffer for motion instructions -static volatile uint8_t block_buffer_head; // Index of the next block to be pushed -static volatile uint8_t block_buffer_tail; // Index of the block to process now -static uint8_t next_buffer_head; // Index of the next buffer head -// static *block_t block_buffer_planned; +static plan_block_t block_buffer[BLOCK_BUFFER_SIZE]; // A ring buffer for motion instructions +static uint8_t block_buffer_tail; // Index of the block to process now +static uint8_t block_buffer_head; // Index of the next block to be pushed +static uint8_t next_buffer_head; // Index of the next buffer head +static uint8_t block_buffer_planned; // Index of the optimally planned block // Define planner variables typedef struct { @@ -47,14 +45,12 @@ typedef struct { // i.e. arcs, canned cycles, and backlash compensation. float previous_unit_vec[N_AXIS]; // Unit vector of previous path line segment float previous_nominal_speed_sqr; // Nominal speed of previous path line segment - float last_x, last_y, last_z; // Target position of previous path line segment } planner_t; static planner_t pl; -// Returns the index of the next block in the ring buffer -// NOTE: Removed modulo (%) operator, which uses an expensive divide and multiplication. -static uint8_t next_block_index(uint8_t block_index) +// Returns the index of the next block in the ring buffer. Also called by stepper segment buffer. +uint8_t plan_next_block_index(uint8_t block_index) { block_index++; if (block_index == BLOCK_BUFFER_SIZE) { block_index = 0; } @@ -63,7 +59,7 @@ static uint8_t next_block_index(uint8_t block_index) // Returns the index of the previous block in the ring buffer -static uint8_t prev_block_index(uint8_t block_index) +static uint8_t plan_prev_block_index(uint8_t block_index) { if (block_index == 0) { block_index = BLOCK_BUFFER_SIZE; } block_index--; @@ -71,292 +67,180 @@ static uint8_t prev_block_index(uint8_t block_index) } -/* STEPPER VELOCITY PROFILE DEFINITION - less than nominal rate-> + - +--------+ <- nominal_rate /|\ - / \ / | \ - initial_rate -> + \ / | + <- next->initial_rate - | + <- next->initial_rate / | | - +-------------+ initial_rate -> +----+--+ - time --> ^ ^ ^ ^ - | | | | - decelerate distance decelerate distance - - Calculates trapezoid parameters for stepper algorithm. Each block velocity profiles can be - described as either a trapezoidal or a triangular shape. The trapezoid occurs when the block - reaches the nominal speed of the block and cruises for a period of time. A triangle occurs - when the nominal speed is not reached within the block. Some other special cases exist, - such as pure ac/de-celeration velocity profiles from beginning to end or a trapezoid that - has no deceleration period when the next block resumes acceleration. - - The following function determines the type of velocity profile and stores the minimum required - information for the stepper algorithm to execute the calculated profiles. In this case, only - the new initial rate and n_steps until deceleration are computed, since the stepper algorithm - already handles acceleration and cruising and just needs to know when to start decelerating. -*/ -static void calculate_trapezoid_for_block(block_t *block, float entry_speed_sqr, float exit_speed_sqr) -{ - // Compute new initial rate for stepper algorithm - block->initial_rate = ceil(sqrt(entry_speed_sqr)*(RANADE_MULTIPLIER/(60*ISR_TICKS_PER_SECOND))); // (mult*mm/isr_tic) - - // TODO: Compute new nominal rate if a feedrate override occurs. - // block->nominal_rate = ceil(feed_rate*(RANADE_MULTIPLIER/(60.0*ISR_TICKS_PER_SECOND))); // (mult*mm/isr_tic) - - // Compute efficiency variable for following calculations. Removes a float divide and multiply. - // TODO: If memory allows, this can be kept in the block buffer since it doesn't change, even after feed holds. - float steps_per_mm_div_2_acc = block->step_event_count/(2*block->acceleration*block->millimeters); - - // First determine intersection distance (in steps) from the exit point for a triangular profile. - // Computes: steps_intersect = steps/mm * ( distance/2 + (v_entry^2-v_exit^2)/(4*acceleration) ) - int32_t intersect_distance = ceil( 0.5*(block->step_event_count + steps_per_mm_div_2_acc*(entry_speed_sqr-exit_speed_sqr)) ); - - // Check if this is a pure acceleration block by a intersection distance less than zero. Also - // prevents signed and unsigned integer conversion errors. - if (intersect_distance <= 0) { - block->decelerate_after = 0; - } else { - // Determine deceleration distance (in steps) from nominal speed to exit speed for a trapezoidal profile. - // Value is never negative. Nominal speed is always greater than or equal to the exit speed. - // Computes: steps_decelerate = steps/mm * ( (v_nominal^2 - v_exit^2)/(2*acceleration) ) - block->decelerate_after = ceil(steps_per_mm_div_2_acc * (block->nominal_speed_sqr - exit_speed_sqr)); - - // The lesser of the two triangle and trapezoid distances always defines the velocity profile. - if (block->decelerate_after > intersect_distance) { block->decelerate_after = intersect_distance; } - - // Finally, check if this is a pure deceleration block. - if (block->decelerate_after > block->step_event_count) { block->decelerate_after = block->step_event_count; } - } -} - - /* PLANNER SPEED DEFINITION +--------+ <- current->nominal_speed / \ current->entry_speed -> + \ - | + <- next->entry_speed + | + <- next->entry_speed (aka exit speed) +-------------+ time --> - Recalculates the motion plan according to the following algorithm: + Recalculates the motion plan according to the following basic guidelines: - 1. Go over every block in reverse order and calculate a junction speed reduction (i.e. block_t.entry_speed) - so that: - a. The junction speed is equal to or less than the maximum junction speed limit - b. No speed reduction within one block requires faster deceleration than the acceleration limits. - c. The last (or newest appended) block is planned from a complete stop. + 1. Go over every feasible block sequentially in reverse order and calculate the junction speeds + (i.e. current->entry_speed) such that: + a. No junction speed exceeds the pre-computed maximum junction speed limit or nominal speeds of + neighboring blocks. + b. A block entry speed cannot exceed one reverse-computed from its exit speed (next->entry_speed) + with a maximum allowable deceleration over the block travel distance. + c. The last (or newest appended) block is planned from a complete stop (an exit speed of zero). 2. Go over every block in chronological (forward) order and dial down junction speed values if - a. The speed increase within one block would require faster acceleration than the acceleration limits. + a. The exit speed exceeds the one forward-computed from its entry speed with the maximum allowable + acceleration over the block travel distance. - When these stages are complete, all blocks have a junction entry speed that will allow all speed changes - to be performed using the overall limiting acceleration value, and where no junction speed is greater - than the max limit. In other words, it just computed the fastest possible velocity profile through all - buffered blocks, where the final buffered block is planned to come to a full stop when the buffer is fully - executed. Finally it will: + When these stages are complete, the planner will have maximized the velocity profiles throughout the all + of the planner blocks, where every block is operating at its maximum allowable acceleration limits. In + other words, for all of the blocks in the planner, the plan is optimal and no further speed improvements + are possible. If a new block is added to the buffer, the plan is recomputed according to the said + guidelines for a new optimal plan. - 3. Convert the plan to data that the stepper algorithm needs. Only block trapezoids adjacent to a - a planner-modified junction speed with be updated, the others are assumed ok as is. + To increase computational efficiency of these guidelines, a set of planner block pointers have been + created to indicate stop-compute points for when the planner guidelines cannot logically make any further + changes or improvements to the plan when in normal operation and new blocks are streamed and added to the + planner buffer. For example, if a subset of sequential blocks in the planner have been planned and are + bracketed by junction velocities at their maximums (or by the first planner block as well), no new block + added to the planner buffer will alter the velocity profiles within them. So we no longer have to compute + them. Or, if a set of sequential blocks from the first block in the planner (or a optimal stop-compute + point) are all accelerating, they are all optimal and can not be altered by a new block added to the + planner buffer, as this will only further increase the plan speed to chronological blocks until a maximum + junction velocity is reached. However, if the operational conditions of the plan changes from infrequently + used feed holds or feedrate overrides, the stop-compute pointers will be reset and the entire plan is + recomputed as stated in the general guidelines. - All planner computations(1)(2) are performed in floating point to minimize numerical round-off errors. Only - when planned values are converted to stepper rate parameters(3), these are integers. If another motion block - is added while executing, the planner will re-plan and update the stored optimal velocity profile as it goes. + Planner buffer index mapping: + - block_buffer_tail: Points to the beginning of the planner buffer. First to be executed or being executed. + - block_buffer_head: Points to the buffer block after the last block in the buffer. Used to indicate whether + the buffer is full or empty. As described for standard ring buffers, this block is always empty. + - next_buffer_head: Points to next planner buffer block after the buffer head block. When equal to the + buffer tail, this indicates the buffer is full. + - block_buffer_planned: Points to the first buffer block after the last optimally planned block for normal + streaming operating conditions. Use for planning optimizations by avoiding recomputing parts of the + planner buffer that don't change with the addition of a new block, as describe above. In addition, + this block can never be less than block_buffer_tail and will always be pushed forward and maintain + this requirement when encountered by the plan_discard_current_block() routine during a cycle. - Conceptually, the planner works like blowing up a balloon, where the balloon is the velocity profile. It's - constrained by the speeds at the beginning and end of the buffer, along with the maximum junction speeds and - nominal speeds of each block. Once a plan is computed, or balloon filled, this is the optimal velocity profile - through all of the motions in the buffer. Whenever a new block is added, this changes some of the limiting - conditions, or how the balloon is filled, so it has to be re-calculated to get the new optimal velocity profile. - - Also, since the planner only computes on what's in the planner buffer, some motions with lots of short line - segments, like arcs, may seem to move slow. This is because there simply isn't enough combined distance traveled - in the entire buffer to accelerate up to the nominal speed and then decelerate to a stop at the end of the - buffer. There are a few simple solutions to this: (1) Maximize the machine acceleration. The planner will be - able to compute higher speed profiles within the same combined distance. (2) Increase line segment(s) distance. - The more combined distance the planner has to use, the faster it can go. (3) Increase the MINIMUM_PLANNER_SPEED. - Not recommended. This will change what speed the planner plans to at the end of the buffer. Can lead to lost - steps when coming to a stop. (4) [BEST] Increase the planner buffer size. The more combined distance, the - bigger the balloon, or faster it can go. But this is not possible for 328p Arduinos because its limited memory - is already maxed out. Future ARM versions should not have this issue, with look-ahead planner blocks numbering - up to a hundred or more. + NOTE: Since the planner only computes on what's in the planner buffer, some motions with lots of short + line segments, like G2/3 arcs or complex curves, may seem to move slow. This is because there simply isn't + enough combined distance traveled in the entire buffer to accelerate up to the nominal speed and then + decelerate to a complete stop at the end of the buffer, as stated by the guidelines. If this happens and + becomes an annoyance, there are a few simple solutions: (1) Maximize the machine acceleration. The planner + will be able to compute higher velocity profiles within the same combined distance. (2) Maximize line + motion(s) distance per block to a desired tolerance. The more combined distance the planner has to use, + the faster it can go. (3) Maximize the planner buffer size. This also will increase the combined distance + for the planner to compute over. It also increases the number of computations the planner has to perform + to compute an optimal plan, so select carefully. The Arduino 328p memory is already maxed out, but future + ARM versions should have enough memory and speed for look-ahead blocks numbering up to a hundred or more. - NOTE: Since this function is constantly re-calculating for every new incoming block, it must be as efficient - as possible. For example, in situations like arc generation or complex curves, the short, rapid line segments - can execute faster than new blocks can be added, and the planner buffer will then starve and empty, leading - to weird hiccup-like jerky motions. */ static void planner_recalculate() -{ - -// float entry_speed_sqr; -// uint8_t block_index = block_buffer_head; -// block_t *previous = NULL; -// block_t *current = NULL; -// block_t *next; -// while (block_index != block_buffer_tail) { -// block_index = prev_block_index( block_index ); -// next = current; -// current = previous; -// previous = &block_buffer[block_index]; -// -// if (next && current) { -// if (next != block_buffer_planned) { -// if (previous == block_buffer_tail) { block_buffer_planned = next; } -// else { -// -// if (current->entry_speed_sqr != current->max_entry_speed_sqr) { -// current->recalculate_flag = true; // Almost always changes. So force recalculate. -// entry_speed_sqr = next->entry_speed_sqr + 2*current->acceleration*current->millimeters; -// if (entry_speed_sqr < current->max_entry_speed_sqr) { -// current->entry_speed_sqr = entry_speed_sqr; -// } else { -// current->entry_speed_sqr = current->max_entry_speed_sqr; -// } -// } else { -// block_buffer_planned = current; -// } -// } -// } else { -// break; -// } -// } -// } -// -// block_index = block_buffer_planned; -// next = &block_buffer[block_index]; -// current = prev_block_index(block_index); -// while (block_index != block_buffer_head) { -// -// // If the current block is an acceleration block, but it is not long enough to complete the -// // full speed change within the block, we need to adjust the exit speed accordingly. Entry -// // speeds have already been reset, maximized, and reverse planned by reverse planner. -// if (current->entry_speed_sqr < next->entry_speed_sqr) { -// // Compute block exit speed based on the current block speed and distance -// // Computes: v_exit^2 = v_entry^2 + 2*acceleration*distance -// entry_speed_sqr = current->entry_speed_sqr + 2*current->acceleration*current->millimeters; -// -// // If it's less than the stored value, update the exit speed and set recalculate flag. -// if (entry_speed_sqr < next->entry_speed_sqr) { -// next->entry_speed_sqr = entry_speed_sqr; -// next->recalculate_flag = true; -// } -// } -// -// // Recalculate if current block entry or exit junction speed has changed. -// if (current->recalculate_flag || next->recalculate_flag) { -// // NOTE: Entry and exit factors always > 0 by all previous logic operations. -// calculate_trapezoid_for_block(current, current->entry_speed_sqr, next->entry_speed_sqr); -// current->recalculate_flag = false; // Reset current only to ensure next trapezoid is computed -// } -// -// current = next; -// next = &block_buffer[block_index]; -// block_index = next_block_index( block_index ); -// } -// -// // Last/newest block in buffer. Exit speed is set with MINIMUM_PLANNER_SPEED. Always recalculated. -// calculate_trapezoid_for_block(next, next->entry_speed_sqr, MINIMUM_PLANNER_SPEED*MINIMUM_PLANNER_SPEED); -// next->recalculate_flag = false; - - // TODO: No over-write protection exists for the executing block. For most cases this has proven to be ok, but - // for feed-rate overrides, something like this is essential. Place a request here to the stepper driver to - // find out where in the planner buffer is the a safe place to begin re-planning from. - -// if (block_buffer_head != block_buffer_tail) { +{ + // Initialize block index to the last block in the planner buffer. + uint8_t block_index = plan_prev_block_index(block_buffer_head); + + // Bail. Can't do anything with one only one plan-able block. + if (block_index == block_buffer_planned) { return; } + + // Reverse Pass: Coarsely maximize all possible deceleration curves back-planning from the last + // block in buffer. Cease planning when the last optimal planned or tail pointer is reached. + // NOTE: Forward pass will later refine and correct the reverse pass to create an optimal plan. float entry_speed_sqr; + plan_block_t *next; + plan_block_t *current = &block_buffer[block_index]; - // Perform reverse planner pass. Skip the head(end) block since it is already initialized, and skip the - // tail(first) block to prevent over-writing of the initial entry speed. - uint8_t block_index = prev_block_index( block_buffer_head ); // Assume buffer is not empty. - block_t *current = &block_buffer[block_index]; // Head block-1 = Newly appended block - block_t *next; - if (block_index != block_buffer_tail) { block_index = prev_block_index( block_index ); } - while (block_index != block_buffer_tail) { - next = current; - current = &block_buffer[block_index]; - - // TODO: Determine maximum entry speed at junction for feedrate overrides, since they can alter - // the planner nominal speeds at any time. This calc could be done in the override handler, but - // this could require an additional variable to be stored to differentiate the programmed nominal - // speeds, max junction speed, and override speeds/scalar. - - // If entry speed is already at the maximum entry speed, no need to recheck. Block is cruising. - // If not, block in state of acceleration or deceleration. Reset entry speed to maximum and - // check for maximum allowable speed reductions to ensure maximum possible planned speed. - if (current->entry_speed_sqr != current->max_entry_speed_sqr) { + // Calculate maximum entry speed for last block in buffer, where the exit speed is always zero. + current->entry_speed_sqr = min( current->max_entry_speed_sqr, 2*current->acceleration*current->millimeters); + + block_index = plan_prev_block_index(block_index); + if (block_index == block_buffer_planned) { // Only two plannable blocks in buffer. Reverse pass complete. + // Check if the first block is the tail. If so, notify stepper to update its current parameters. + if (block_index == block_buffer_tail) { st_update_plan_block_parameters(); } + } else { // Three or more plan-able blocks + while (block_index != block_buffer_planned) { + next = current; + current = &block_buffer[block_index]; + block_index = plan_prev_block_index(block_index); - current->entry_speed_sqr = current->max_entry_speed_sqr; - current->recalculate_flag = true; // Almost always changes. So force recalculate. + // Check if next block is the tail block(=planned block). If so, update current stepper parameters. + if (block_index == block_buffer_tail) { st_update_plan_block_parameters(); } - if (next->entry_speed_sqr < current->max_entry_speed_sqr) { - // Computes: v_entry^2 = v_exit^2 + 2*acceleration*distance + // Compute maximum entry speed decelerating over the current block from its exit speed. + if (current->entry_speed_sqr != current->max_entry_speed_sqr) { entry_speed_sqr = next->entry_speed_sqr + 2*current->acceleration*current->millimeters; if (entry_speed_sqr < current->max_entry_speed_sqr) { current->entry_speed_sqr = entry_speed_sqr; + } else { + current->entry_speed_sqr = current->max_entry_speed_sqr; } - } - } - block_index = prev_block_index( block_index ); - } + } + } + } - // Perform forward planner pass. Begins junction speed adjustments after tail(first) block. - // Also recalculate trapezoids, block by block, as the forward pass completes the plan. - block_index = next_block_index(block_buffer_tail); - next = &block_buffer[block_buffer_tail]; // Places tail(first) block into current + // Forward Pass: Forward plan the acceleration curve from the planned pointer onward. + // Also scans for optimal plan breakpoints and appropriately updates the planned pointer. + next = &block_buffer[block_buffer_planned]; // Begin at buffer planned pointer + block_index = plan_next_block_index(block_buffer_planned); while (block_index != block_buffer_head) { current = next; next = &block_buffer[block_index]; - - // If the current block is an acceleration block, but it is not long enough to complete the - // full speed change within the block, we need to adjust the exit speed accordingly. Entry - // speeds have already been reset, maximized, and reverse planned by reverse planner. - if (current->entry_speed_sqr < next->entry_speed_sqr) { - // Compute block exit speed based on the current block speed and distance - // Computes: v_exit^2 = v_entry^2 + 2*acceleration*distance - entry_speed_sqr = current->entry_speed_sqr + 2*current->acceleration*current->millimeters; - - // If it's less than the stored value, update the exit speed and set recalculate flag. - if (entry_speed_sqr < next->entry_speed_sqr) { - next->entry_speed_sqr = entry_speed_sqr; - next->recalculate_flag = true; - } + + // Any acceleration detected in the forward pass automatically moves the optimal planned + // pointer forward, since everything before this is all optimal. In other words, nothing + // can improve the plan from the buffer tail to the planned pointer by logic. + if (current->entry_speed_sqr < next->entry_speed_sqr) { + entry_speed_sqr = current->entry_speed_sqr + 2*current->acceleration*current->millimeters; + // If true, current block is full-acceleration and we can move the planned pointer forward. + if (entry_speed_sqr < next->entry_speed_sqr) { + next->entry_speed_sqr = entry_speed_sqr; // Always <= max_entry_speed_sqr. Backward pass sets this. + block_buffer_planned = block_index; // Set optimal plan pointer. } - - // Recalculate if current block entry or exit junction speed has changed. - if (current->recalculate_flag || next->recalculate_flag) { - // NOTE: Entry and exit factors always > 0 by all previous logic operations. - calculate_trapezoid_for_block(current, current->entry_speed_sqr, next->entry_speed_sqr); - current->recalculate_flag = false; // Reset current only to ensure next trapezoid is computed - } - - block_index = next_block_index( block_index ); - } - - // Last/newest block in buffer. Exit speed is set with MINIMUM_PLANNER_SPEED. Always recalculated. - calculate_trapezoid_for_block(next, next->entry_speed_sqr, MINIMUM_PLANNER_SPEED*MINIMUM_PLANNER_SPEED); - next->recalculate_flag = false; -// } + } + + // Any block set at its maximum entry speed also creates an optimal plan up to this + // point in the buffer. When the plan is bracketed by either the beginning of the + // buffer and a maximum entry speed or two maximum entry speeds, every block in between + // cannot logically be further improved. Hence, we don't have to recompute them anymore. + if (next->entry_speed_sqr == next->max_entry_speed_sqr) { block_buffer_planned = block_index; } + block_index = plan_next_block_index( block_index ); + } } -void plan_init() + +void plan_reset() { - block_buffer_tail = block_buffer_head; - next_buffer_head = next_block_index(block_buffer_head); -// block_buffer_planned = block_buffer_head; memset(&pl, 0, sizeof(pl)); // Clear planner struct + block_buffer_tail = 0; + block_buffer_head = 0; // Empty = tail + next_buffer_head = 1; // plan_next_block_index(block_buffer_head) + block_buffer_planned = 0; // = block_buffer_tail; } -inline void plan_discard_current_block() + +void plan_discard_current_block() { - if (block_buffer_head != block_buffer_tail) { - block_buffer_tail = next_block_index( block_buffer_tail ); + if (block_buffer_head != block_buffer_tail) { // Discard non-empty buffer. + uint8_t block_index = plan_next_block_index( block_buffer_tail ); + // Push block_buffer_planned pointer, if encountered. + if (block_buffer_tail == block_buffer_planned) { block_buffer_planned = block_index; } + block_buffer_tail = block_index; } } -inline block_t *plan_get_current_block() + +plan_block_t *plan_get_current_block() { - if (block_buffer_head == block_buffer_tail) { return(NULL); } + if (block_buffer_head == block_buffer_tail) { return(NULL); } // Buffer empty return(&block_buffer[block_buffer_tail]); } + +float plan_get_exec_block_exit_speed() +{ + uint8_t block_index = plan_next_block_index(block_buffer_tail); + if (block_index == block_buffer_head) { return( 0.0 ); } + return( sqrt( block_buffer[block_index].entry_speed_sqr ) ); +} + + // Returns the availability status of the block ring buffer. True, if full. uint8_t plan_check_full_buffer() { @@ -364,185 +248,170 @@ uint8_t plan_check_full_buffer() return(false); } -// Block until all buffered steps are executed or in a cycle state. Works with feed hold -// during a synchronize call, if it should happen. Also, waits for clean cycle end. -void plan_synchronize() + +/* Add a new linear movement to the buffer. target[N_AXIS] is the signed, absolute target position + in millimeters. Feed rate specifies the speed of the motion. If feed rate is inverted, the feed + rate is taken to mean "frequency" and would complete the operation in 1/feed_rate minutes. + All position data passed to the planner must be in terms of machine position to keep the planner + independent of any coordinate system changes and offsets, which are handled by the g-code parser. + NOTE: Assumes buffer is available. Buffer checks are handled at a higher level by motion_control. + In other words, the buffer head is never equal to the buffer tail. Also the feed rate input value + is used in three ways: as a normal feed rate if invert_feed_rate is false, as inverse time if + invert_feed_rate is true, or as seek/rapids rate if the feed_rate value is negative (and + invert_feed_rate always false). */ +#ifdef USE_LINE_NUMBERS +void plan_buffer_line(float *target, float feed_rate, uint8_t invert_feed_rate, int32_t line_number) +#else +void plan_buffer_line(float *target, float feed_rate, uint8_t invert_feed_rate) +#endif { - while (plan_get_current_block() || sys.state == STATE_CYCLE) { - protocol_execute_runtime(); // Check and execute run-time commands - if (sys.abort) { return; } // Check for system abort - } -} + // Prepare and initialize new block + plan_block_t *block = &block_buffer[block_buffer_head]; + block->step_event_count = 0; + block->millimeters = 0; + block->direction_bits = 0; + block->acceleration = SOME_LARGE_VALUE; // Scaled down to maximum acceleration later + #ifdef USE_LINE_NUMBERS + block->line_number = line_number; + #endif -// Add a new linear movement to the buffer. x, y and z is the signed, absolute target position in -// millimeters. Feed rate specifies the speed of the motion. If feed rate is inverted, the feed -// rate is taken to mean "frequency" and would complete the operation in 1/feed_rate minutes. -// All position data passed to the planner must be in terms of machine position to keep the planner -// independent of any coordinate system changes and offsets, which are handled by the g-code parser. -// NOTE: Assumes buffer is available. Buffer checks are handled at a higher level by motion_control. -// Also the feed rate input value is used in three ways: as a normal feed rate if invert_feed_rate -// is false, as inverse time if invert_feed_rate is true, or as seek/rapids rate if the feed_rate -// value is negative (and invert_feed_rate always false). -void plan_buffer_line(float x, float y, float z, float feed_rate, uint8_t invert_feed_rate) -{ - // Prepare to set up new block - block_t *block = &block_buffer[block_buffer_head]; - - // Calculate target position in absolute steps - int32_t target[N_AXIS]; - target[X_AXIS] = lround(x*settings.steps_per_mm[X_AXIS]); - target[Y_AXIS] = lround(y*settings.steps_per_mm[Y_AXIS]); - target[Z_AXIS] = lround(z*settings.steps_per_mm[Z_AXIS]); + // Compute and store initial move distance data. + // TODO: After this for-loop, we don't touch the stepper algorithm data. Might be a good idea + // to try to keep these types of things completely separate from the planner for portability. + int32_t target_steps[N_AXIS]; + float unit_vec[N_AXIS], delta_mm; + uint8_t idx; + for (idx=0; idxsteps_x = labs(target[X_AXIS]-pl.position[X_AXIS]); - block->steps_y = labs(target[Y_AXIS]-pl.position[Y_AXIS]); - block->steps_z = labs(target[Z_AXIS]-pl.position[Z_AXIS]); - block->step_event_count = max(block->steps_x, max(block->steps_y, block->steps_z)); - - // Bail if this is a zero-length block - if (block->step_event_count == 0) { return; }; + // Number of steps for each axis and determine max step events + block->steps[idx] = labs(target_steps[idx]-pl.position[idx]); + block->step_event_count = max(block->step_event_count, block->steps[idx]); + + // Compute individual axes distance for move and prep unit vector calculations. + // NOTE: Computes true distance from converted step values. + delta_mm = (target_steps[idx] - pl.position[idx])/settings.steps_per_mm[idx]; + unit_vec[idx] = delta_mm; // Store unit vector numerator. Denominator computed later. + + // Set direction bits. Bit enabled always means direction is negative. + if (delta_mm < 0 ) { block->direction_bits |= get_direction_mask(idx); } + + // Incrementally compute total move distance by Euclidean norm. First add square of each term. + block->millimeters += delta_mm*delta_mm; + } + block->millimeters = sqrt(block->millimeters); // Complete millimeters calculation with sqrt() + + // Bail if this is a zero-length block. Highly unlikely to occur. + if (block->step_event_count == 0) { return; } - // Compute path vector in terms of absolute step target and current positions - float delta_mm[N_AXIS]; - delta_mm[X_AXIS] = x-pl.last_x; - delta_mm[Y_AXIS] = y-pl.last_y; - delta_mm[Z_AXIS] = z-pl.last_z; - block->millimeters = sqrt(delta_mm[X_AXIS]*delta_mm[X_AXIS] + delta_mm[Y_AXIS]*delta_mm[Y_AXIS] + - delta_mm[Z_AXIS]*delta_mm[Z_AXIS]); - // Adjust feed_rate value to mm/min depending on type of rate input (normal, inverse time, or rapids) // TODO: Need to distinguish a rapids vs feed move for overrides. Some flag of some sort. if (feed_rate < 0) { feed_rate = SOME_LARGE_VALUE; } // Scaled down to absolute max/rapids rate later else if (invert_feed_rate) { feed_rate = block->millimeters/feed_rate; } - // Calculate the unit vector of the line move and the block maximum feed rate and acceleration limited - // by the maximum possible values. Block rapids rates are computed or feed rates are scaled down so - // they don't exceed the maximum axes velocities. The block acceleration is maximized based on direction - // and axes properties as well. + // Calculate the unit vector of the line move and the block maximum feed rate and acceleration scaled + // down such that no individual axes maximum values are exceeded with respect to the line direction. // NOTE: This calculation assumes all axes are orthogonal (Cartesian) and works with ABC-axes, // if they are also orthogonal/independent. Operates on the absolute value of the unit vector. - uint8_t i; - float unit_vec[N_AXIS], inverse_unit_vec_value; + float inverse_unit_vec_value; float inverse_millimeters = 1.0/block->millimeters; // Inverse millimeters to remove multiple float divides - block->acceleration = SOME_LARGE_VALUE; // Scaled down to maximum acceleration in loop - for (i=0; iacceleration = min(block->acceleration,settings.acceleration[i]*inverse_unit_vec_value); + float junction_cos_theta = 0; + for (idx=0; idxacceleration = min(block->acceleration,settings.acceleration[idx]*inverse_unit_vec_value); + + // Incrementally compute cosine of angle between previous and current path. Cos(theta) of the junction + // between the current move and the previous move is simply the dot product of the two unit vectors, + // where prev_unit_vec is negative. Used later to compute maximum junction speed. + junction_cos_theta -= pl.previous_unit_vec[idx] * unit_vec[idx]; } } - - // Compute nominal speed and rates - block->nominal_speed_sqr = feed_rate*feed_rate; // (mm/min)^2. Always > 0 - block->nominal_rate = ceil(feed_rate*(RANADE_MULTIPLIER/(60.0*ISR_TICKS_PER_SECOND))); // (mult*mm/isr_tic) - - // Compute the acceleration and distance traveled per step event for the stepper algorithm. - block->rate_delta = ceil(block->acceleration* - ((RANADE_MULTIPLIER/(60.0*60.0))/(ISR_TICKS_PER_SECOND*ACCELERATION_TICKS_PER_SECOND))); // (mult*mm/isr_tic/accel_tic) - block->d_next = ceil((block->millimeters*RANADE_MULTIPLIER)/block->step_event_count); // (mult*mm/step) - // Compute direction bits. Bit enabled always means direction is negative. - block->direction_bits = 0; - if (unit_vec[X_AXIS] < 0) { block->direction_bits |= (1<direction_bits |= (1<direction_bits |= (1<entry_speed_sqr = 0.0; + block->max_junction_speed_sqr = 0.0; // Starting from rest. Enforce start from zero velocity. + + } else { + /* + Compute maximum allowable entry speed at junction by centripetal acceleration approximation. + Let a circle be tangent to both previous and current path line segments, where the junction + deviation is defined as the distance from the junction to the closest edge of the circle, + colinear with the circle center. The circular segment joining the two paths represents the + path of centripetal acceleration. Solve for max velocity based on max acceleration about the + radius of the circle, defined indirectly by junction deviation. This may be also viewed as + path width or max_jerk in the previous grbl version. This approach does not actually deviate + from path, but used as a robust way to compute cornering speeds, as it takes into account the + nonlinearities of both the junction angle and junction velocity. - // Compute maximum allowable entry speed at junction by centripetal acceleration approximation. - // Let a circle be tangent to both previous and current path line segments, where the junction - // deviation is defined as the distance from the junction to the closest edge of the circle, - // colinear with the circle center. The circular segment joining the two paths represents the - // path of centripetal acceleration. Solve for max velocity based on max acceleration about the - // radius of the circle, defined indirectly by junction deviation. This may be also viewed as - // path width or max_jerk in the previous grbl version. This approach does not actually deviate - // from path, but used as a robust way to compute cornering speeds, as it takes into account the - // nonlinearities of both the junction angle and junction velocity. - // NOTE: If the junction deviation value is finite, Grbl executes the motions in an exact path - // mode (G61). If the junction deviation value is zero, Grbl will execute the motion in an exact - // stop mode (G61.1) manner. In the future, if continuous mode (G64) is desired, the math here - // is exactly the same. Instead of motioning all the way to junction point, the machine will - // just follow the arc circle defined here. The Arduino doesn't have the CPU cycles to perform - // a continuous mode path, but ARM-based microcontrollers most certainly do. + NOTE: If the junction deviation value is finite, Grbl executes the motions in an exact path + mode (G61). If the junction deviation value is zero, Grbl will execute the motion in an exact + stop mode (G61.1) manner. In the future, if continuous mode (G64) is desired, the math here + is exactly the same. Instead of motioning all the way to junction point, the machine will + just follow the arc circle defined here. The Arduino doesn't have the CPU cycles to perform + a continuous mode path, but ARM-based microcontrollers most certainly do. + + NOTE: The max junction speed is a fixed value, since machine acceleration limits cannot be + changed dynamically during operation nor can the line move geometry. This must be kept in + memory in the event of a feedrate override changing the nominal speeds of blocks, which can + change the overall maximum entry speed conditions of all blocks. + */ + // NOTE: Computed without any expensive trig, sin() or acos(), by trig half angle identity of cos(theta). + float sin_theta_d2 = sqrt(0.5*(1.0-junction_cos_theta)); // Trig half angle identity. Always positive. - // Skip first block or when previous_nominal_speed is used as a flag for homing and offset cycles. - block->max_entry_speed_sqr = MINIMUM_PLANNER_SPEED*MINIMUM_PLANNER_SPEED; - if ((block_buffer_head != block_buffer_tail) && (pl.previous_nominal_speed_sqr > 0.0)) { - // Compute cosine of angle between previous and current path. (prev_unit_vec is negative) - // NOTE: Max junction velocity is computed without sin() or acos() by trig half angle identity. - float cos_theta = - pl.previous_unit_vec[X_AXIS] * unit_vec[X_AXIS] - - pl.previous_unit_vec[Y_AXIS] * unit_vec[Y_AXIS] - - pl.previous_unit_vec[Z_AXIS] * unit_vec[Z_AXIS] ; - - // Skip and use default max junction speed for 0 degree acute junction. - if (cos_theta < 0.95) { - block->max_entry_speed_sqr = min(block->nominal_speed_sqr,pl.previous_nominal_speed_sqr); - // Skip and avoid divide by zero for straight junctions at 180 degrees. Limit to min() of nominal speeds. - if (cos_theta > -0.95) { - // Compute maximum junction velocity based on maximum acceleration and junction deviation - float sin_theta_d2 = sqrt(0.5*(1.0-cos_theta)); // Trig half angle identity. Always positive. - block->max_entry_speed_sqr = min(block->max_entry_speed_sqr, - block->acceleration * settings.junction_deviation * sin_theta_d2/(1.0-sin_theta_d2)); - } - } - } - - // Initialize block entry speed. Compute block entry velocity backwards from user-defined MINIMUM_PLANNER_SPEED. - // TODO: This could be moved to the planner recalculate function. - block->entry_speed_sqr = min( block->max_entry_speed_sqr, - MINIMUM_PLANNER_SPEED*MINIMUM_PLANNER_SPEED + 2*block->acceleration*block->millimeters); - - // Set new block to be recalculated for conversion to stepper data. - block->recalculate_flag = true; + // TODO: Technically, the acceleration used in calculation needs to be limited by the minimum of the + // two junctions. However, this shouldn't be a significant problem except in extreme circumstances. + block->max_junction_speed_sqr = max( MINIMUM_JUNCTION_SPEED*MINIMUM_JUNCTION_SPEED, + (block->acceleration * settings.junction_deviation * sin_theta_d2)/(1.0-sin_theta_d2) ); + } + // Store block nominal speed + block->nominal_speed_sqr = feed_rate*feed_rate; // (mm/min). Always > 0 + + // Compute the junction maximum entry based on the minimum of the junction speed and neighboring nominal speeds. + block->max_entry_speed_sqr = min(block->max_junction_speed_sqr, + min(block->nominal_speed_sqr,pl.previous_nominal_speed_sqr)); + // Update previous path unit_vector and nominal speed (squared) memcpy(pl.previous_unit_vec, unit_vec, sizeof(unit_vec)); // pl.previous_unit_vec[] = unit_vec[] pl.previous_nominal_speed_sqr = block->nominal_speed_sqr; // Update planner position - memcpy(pl.position, target, sizeof(target)); // pl.position[] = target[] - pl.last_x = x; - pl.last_y = y; - pl.last_z = z; + memcpy(pl.position, target_steps, sizeof(target_steps)); // pl.position[] = target_steps[] - // Update buffer head and next buffer head indices + // New block is all set. Update buffer head and next buffer head indices. block_buffer_head = next_buffer_head; - next_buffer_head = next_block_index(block_buffer_head); - - planner_recalculate(); + next_buffer_head = plan_next_block_index(block_buffer_head); + + // Finish up by recalculating the plan with the new block. + planner_recalculate(); } + // Reset the planner position vectors. Called by the system abort/initialization routine. -void plan_set_current_position(int32_t x, int32_t y, int32_t z) +void plan_sync_position() { - pl.position[X_AXIS] = x; - pl.position[Y_AXIS] = y; - pl.position[Z_AXIS] = z; - pl.last_x = x/settings.steps_per_mm[X_AXIS]; - pl.last_y = y/settings.steps_per_mm[Y_AXIS]; - pl.last_z = z/settings.steps_per_mm[Z_AXIS]; + uint8_t idx; + for (idx=0; idxmillimeters = (block->millimeters*step_events_remaining)/block->step_event_count; - block->step_event_count = step_events_remaining; - - // Re-plan from a complete stop. Reset planner entry speeds and flags. - block->entry_speed_sqr = 0.0; - block->max_entry_speed_sqr = 0.0; - block->recalculate_flag = true; + // Re-plan from a complete stop. Reset planner entry speeds and buffer planned pointer. + st_update_plan_block_parameters(); + block_buffer_planned = block_buffer_tail; planner_recalculate(); } diff --git a/planner.h b/planner.h index ce5126d..0ecfad1 100644 --- a/planner.h +++ b/planner.h @@ -2,8 +2,8 @@ planner.h - buffers movement commands and manages the acceleration profile plan Part of Grbl + Copyright (c) 2011-2014 Sungeun K. Jeon Copyright (c) 2009-2011 Simen Svale Skogsrud - Copyright (c) 2011-2013 Sungeun K. Jeon Grbl is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -21,62 +21,75 @@ #ifndef planner_h #define planner_h - + +#include "system.h" + // The number of linear motions that can be in the plan at any give time #ifndef BLOCK_BUFFER_SIZE - #define BLOCK_BUFFER_SIZE 18 + #ifdef USE_LINE_NUMBERS + #define BLOCK_BUFFER_SIZE 16 + #else + #define BLOCK_BUFFER_SIZE 18 + #endif #endif -// This struct is used when buffering the setup for each linear movement "nominal" values are as specified in -// the source g-code and may never actually be reached if acceleration management is active. +// This struct stores a linear movement of a g-code block motion with its critical "nominal" values +// are as specified in the source g-code. typedef struct { - // Fields used by the bresenham algorithm for tracing the line - uint8_t direction_bits; // The direction bit set for this block (refers to *_DIRECTION_BIT in config.h) - uint32_t steps_x, steps_y, steps_z; // Step count along each axis - int32_t step_event_count; // The number of step events required to complete this block + // NOTE: Used by stepper algorithm to execute the block correctly. Do not alter these values. + uint8_t direction_bits; // The direction bit set for this block (refers to *_DIRECTION_BIT in config.h) + uint32_t steps[N_AXIS]; // Step count along each axis + uint32_t step_event_count; // The maximum step axis count and number of steps required to complete this block. // Fields used by the motion planner to manage acceleration - float nominal_speed_sqr; // The nominal speed for this block in mm/min - float entry_speed_sqr; // Entry speed at previous-current block junction in mm/min - float max_entry_speed_sqr; // Maximum allowable junction entry speed in mm/min - float millimeters; // The total travel of this block in mm - float acceleration; - uint8_t recalculate_flag; // Planner flag to recalculate trapezoids on entry junction + float entry_speed_sqr; // The current planned entry speed at block junction in (mm/min)^2 + float max_entry_speed_sqr; // Maximum allowable entry speed based on the minimum of junction limit and + // neighboring nominal speeds with overrides in (mm/min)^2 + float max_junction_speed_sqr; // Junction entry speed limit based on direction vectors in (mm/min)^2 + float nominal_speed_sqr; // Axis-limit adjusted nominal speed for this block in (mm/min)^2 + float acceleration; // Axis-limit adjusted line acceleration in (mm/min^2) + float millimeters; // The remaining distance for this block to be executed in (mm) + // uint8_t max_override; // Maximum override value based on axis speed limits + + #ifdef USE_LINE_NUMBERS + int32_t line_number; + #endif +} plan_block_t; - // Settings for the trapezoid generator - uint32_t initial_rate; // The step rate at start of block - int32_t rate_delta; // The steps/minute to add or subtract when changing speed (must be positive) - uint32_t decelerate_after; // The index of the step event on which to start decelerating - uint32_t nominal_rate; // The nominal step rate for this block in step_events/minute - uint32_t d_next; // Scaled distance to next step -} block_t; -// Initialize the motion plan subsystem -void plan_init(); +// Initialize and reset the motion plan subsystem +void plan_reset(); -// Add a new linear movement to the buffer. x, y and z is the signed, absolute target position in -// millimaters. Feed rate specifies the speed of the motion. If feed rate is inverted, the feed +// Add a new linear movement to the buffer. target[N_AXIS] is the signed, absolute target position +// in millimeters. Feed rate specifies the speed of the motion. If feed rate is inverted, the feed // rate is taken to mean "frequency" and would complete the operation in 1/feed_rate minutes. -void plan_buffer_line(float x, float y, float z, float feed_rate, uint8_t invert_feed_rate); +#ifdef USE_LINE_NUMBERS +void plan_buffer_line(float *target, float feed_rate, uint8_t invert_feed_rate, int32_t line_number); +#else +void plan_buffer_line(float *target, float feed_rate, uint8_t invert_feed_rate); +#endif // Called when the current block is no longer needed. Discards the block and makes the memory // availible for new blocks. void plan_discard_current_block(); // Gets the current block. Returns NULL if buffer empty -block_t *plan_get_current_block(); +plan_block_t *plan_get_current_block(); + +// Called periodically by step segment buffer. Mostly used internally by planner. +uint8_t plan_next_block_index(uint8_t block_index); + +// Called by step segment buffer when computing executing block velocity profile. +float plan_get_exec_block_exit_speed(); // Reset the planner position vector (in steps) -void plan_set_current_position(int32_t x, int32_t y, int32_t z); +void plan_sync_position(); // Reinitialize plan with a partially completed block -void plan_cycle_reinitialize(int32_t step_events_remaining); +void plan_cycle_reinitialize(); // Returns the status of the block ring buffer. True, if buffer is full. uint8_t plan_check_full_buffer(); -// Block until all buffered steps are executed -void plan_synchronize(); - #endif diff --git a/print.c b/print.c index 2f820d5..a3c46cd 100644 --- a/print.c +++ b/print.c @@ -2,8 +2,8 @@ print.c - Functions for formatting output strings Part of Grbl + Copyright (c) 2011-2014 Sungeun K. Jeon Copyright (c) 2009-2011 Simen Svale Skogsrud - Copyright (c) 2011-2012 Sungeun K. Jeon Grbl is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -22,12 +22,11 @@ /* This code was initially inspired by the wiring_serial module by David A. Mellis which used to be a part of the Arduino project. */ - -#include -#include "config.h" +#include "system.h" #include "serial.h" #include "settings.h" + void printString(const char *s) { while (*s) @@ -77,32 +76,52 @@ void print_uint8_base2(uint8_t n) serial_write('0' + buf[i - 1]); } -static void print_uint32_base10(unsigned long n) +void print_uint8_base10(uint8_t n) { - unsigned char buf[10]; - uint8_t i = 0; - if (n == 0) { serial_write('0'); return; } + + unsigned char buf[3]; + uint8_t i = 0; + + while (n > 0) { + buf[i++] = n % 10 + '0'; + n /= 10; + } + + for (; i > 0; i--) + serial_write(buf[i - 1]); +} + +void print_uint32_base10(unsigned long n) +{ + if (n == 0) { + serial_write('0'); + return; + } + + unsigned char buf[10]; + uint8_t i = 0; while (n > 0) { - buf[i++] = n % 10 + '0'; + buf[i++] = n % 10; n /= 10; } for (; i > 0; i--) - serial_write(buf[i-1]); + serial_write('0' + buf[i-1]); } void printInteger(long n) { if (n < 0) { serial_write('-'); - n = -n; + print_uint32_base10((-n)); + } else { + print_uint32_base10(n); } - print_uint32_base10(n); } // Convert float to string by immediately converting to a long integer, which contains @@ -110,14 +129,14 @@ void printInteger(long n) // may be set by the user. The integer is then efficiently converted to a string. // NOTE: AVR '%' and '/' integer operations are very efficient. Bitshifting speed-up // techniques are actually just slightly slower. Found this out the hard way. -void printFloat(float n) +void printFloat(float n, uint8_t decimal_places) { if (n < 0) { serial_write('-'); n = -n; } - uint8_t decimals = settings.decimal_places; + uint8_t decimals = decimal_places; while (decimals >= 2) { // Quickly convert values expected to be E0 to E-4. n *= 100; decimals -= 2; @@ -129,16 +148,16 @@ void printFloat(float n) unsigned char buf[10]; uint8_t i = 0; uint32_t a = (long)n; - buf[settings.decimal_places] = '.'; // Place decimal point, even if decimal places are zero. + buf[decimal_places] = '.'; // Place decimal point, even if decimal places are zero. while(a > 0) { - if (i == settings.decimal_places) { i++; } // Skip decimal point location + if (i == decimal_places) { i++; } // Skip decimal point location buf[i++] = (a % 10) + '0'; // Get digit a /= 10; } - while (i < settings.decimal_places) { + while (i < decimal_places) { buf[i++] = '0'; // Fill in zeros to decimal point for (n < 1) } - if (i == settings.decimal_places) { // Fill in leading zero, if needed. + if (i == decimal_places) { // Fill in leading zero, if needed. i++; buf[i++] = '0'; } @@ -147,3 +166,27 @@ void printFloat(float n) for (; i > 0; i--) serial_write(buf[i-1]); } + + +// Floating value printing handlers for special variables types used in Grbl and are defined +// in the config.h. +// - CoordValue: Handles all position or coordinate values in inches or mm reporting. +// - RateValue: Handles feed rate and current velocity in inches or mm reporting. +// - SettingValue: Handles all floating point settings values (always in mm.) +void printFloat_CoordValue(float n) { + if (bit_istrue(settings.flags,BITFLAG_REPORT_INCHES)) { + printFloat(n*INCH_PER_MM,N_DECIMAL_COORDVALUE_INCH); + } else { + printFloat(n,N_DECIMAL_COORDVALUE_MM); + } +} + +void printFloat_RateValue(float n) { + if (bit_istrue(settings.flags,BITFLAG_REPORT_INCHES)) { + printFloat(n*INCH_PER_MM,N_DECIMAL_RATEVALUE_INCH); + } else { + printFloat(n,N_DECIMAL_RATEVALUE_MM); + } +} + +void printFloat_SettingValue(float n) { printFloat(n,N_DECIMAL_SETTINGVALUE); } diff --git a/print.h b/print.h index 9983aee..616b98d 100644 --- a/print.h +++ b/print.h @@ -2,8 +2,8 @@ print.h - Functions for formatting output strings Part of Grbl + Copyright (c) 2011-2014 Sungeun K. Jeon Copyright (c) 2009-2011 Simen Svale Skogsrud - Copyright (c) 2011-2012 Sungeun K. Jeon Grbl is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -31,8 +31,22 @@ void printPgmString(const char *s); void printInteger(long n); +void print_uint32_base10(uint32_t n); + void print_uint8_base2(uint8_t n); -void printFloat(float n); +void print_uint8_base10(uint8_t n); + +void printFloat(float n, uint8_t decimal_places); + +// Floating value printing handlers for special variables types used in Grbl. +// - CoordValue: Handles all position or coordinate values in inches or mm reporting. +// - RateValue: Handles feed rate and current velocity in inches or mm reporting. +// - SettingValue: Handles all floating point settings values (always in mm.) +void printFloat_CoordValue(float n); + +void printFloat_RateValue(float n); + +void printFloat_SettingValue(float n); #endif \ No newline at end of file diff --git a/probe.c b/probe.c new file mode 100644 index 0000000..84ec01d --- /dev/null +++ b/probe.c @@ -0,0 +1,51 @@ +/* + probe.c - code pertaining to probing methods + Part of Grbl + + Copyright (c) 2014 Sungeun K. Jeon + + Grbl is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Grbl is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Grbl. If not, see . +*/ + +#include "system.h" +#include "probe.h" + + +// Probe pin initialization routine. +void probe_init() +{ + PROBE_DDR &= ~(PROBE_MASK); // Configure as input pins + PROBE_PORT |= PROBE_MASK; // Enable internal pull-up resistors. Normal high operation. +} + + +// Returns the probe pin state. Triggered = true. Called by gcode parser and probe state monitor. +uint8_t probe_get_state() +{ + return(!(PROBE_PIN & PROBE_MASK)); +} + +// Monitors probe pin state and records the system position when detected. Called by the +// stepper ISR per ISR tick. +// NOTE: This function must be extremely efficient as to not bog down the stepper ISR. +void probe_state_monitor() +{ + if (sys.probe_state == PROBE_ACTIVE) { + if (probe_get_state()) { + sys.probe_state = PROBE_OFF; + memcpy(sys.probe_position, sys.position, sizeof(float)*N_AXIS); + bit_true(sys.execute, EXEC_FEED_HOLD); + } + } +} diff --git a/probe.h b/probe.h new file mode 100644 index 0000000..294c01b --- /dev/null +++ b/probe.h @@ -0,0 +1,39 @@ +/* + probe.h - code pertaining to probing methods + Part of Grbl + + Copyright (c) 2014 Sungeun K. Jeon + + Grbl is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Grbl is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Grbl. If not, see . +*/ + +#ifndef probe_h +#define probe_h + +// Values that define the probing state machine. +#define PROBE_OFF 0 // No probing. (Must be zero.) +#define PROBE_ACTIVE 1 // Actively watching the input pin. + + +// Probe pin initialization routine. +void probe_init(); + +// Returns probe pin state. +uint8_t probe_get_state(); + +// Monitors probe pin state and records the system position when detected. Called by the +// stepper ISR per ISR tick. +void probe_state_monitor(); + +#endif diff --git a/protocol.c b/protocol.c index 56aad3c..9507ca8 100644 --- a/protocol.c +++ b/protocol.c @@ -1,9 +1,9 @@ /* - protocol.c - the serial protocol master control unit + protocol.c - controls Grbl execution protocol and procedures Part of Grbl + Copyright (c) 2011-2014 Sungeun K. Jeon Copyright (c) 2009-2011 Simen Svale Skogsrud - Copyright (c) 2011-2013 Sungeun K. Jeon Grbl is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -19,70 +19,151 @@ along with Grbl. If not, see . */ -#include -#include +#include "system.h" +#include "serial.h" +#include "settings.h" #include "protocol.h" #include "gcode.h" -#include "serial.h" -#include "print.h" -#include "settings.h" -#include "config.h" -#include "nuts_bolts.h" +#include "planner.h" #include "stepper.h" -#include "report.h" #include "motion_control.h" +#include "report.h" + static char line[LINE_BUFFER_SIZE]; // Line to be executed. Zero-terminated. -static uint8_t char_counter; // Last character counter in line variable. -static uint8_t iscomment; // Comment/block delete flag for processor to ignore comment characters. -void protocol_init() -{ - char_counter = 0; // Reset line input - iscomment = false; - report_init_message(); // Welcome message - - PINOUT_DDR &= ~(PINOUT_MASK); // Set as input pins - PINOUT_PORT |= PINOUT_MASK; // Enable internal pull-up resistors. Normal high operation. - PINOUT_PCMSK |= PINOUT_MASK; // Enable specific pins of the Pin Change Interrupt - PCICR |= (1 << PINOUT_INT); // Enable Pin Change Interrupt -} +// Directs and executes one line of formatted input from protocol_process. While mostly +// incoming streaming g-code blocks, this also directs and executes Grbl internal commands, +// such as settings, initiating the homing cycle, and toggling switch states. +static void protocol_execute_line(char *line) +{ + protocol_execute_runtime(); // Runtime command check point. + if (sys.abort) { return; } // Bail to calling function upon system abort -// Executes user startup script, if stored. -void protocol_execute_startup() -{ - uint8_t n; - for (n=0; n < N_STARTUP_LINE; n++) { - if (!(settings_read_startup_line(n, line))) { - report_status_message(STATUS_SETTING_READ_FAIL); - } else { - if (line[0] != 0) { - printString(line); // Echo startup line to indicate execution. - report_status_message(gc_execute_line(line)); - } - } - } -} + if (line[0] == 0) { + // Empty or comment line. Send status message for syncing purposes. + report_status_message(STATUS_OK); -// Pin change interrupt for pin-out commands, i.e. cycle start, feed hold, and reset. Sets -// only the runtime command execute variable to have the main program execute these when -// its ready. This works exactly like the character-based runtime commands when picked off -// directly from the incoming serial data stream. -ISR(PINOUT_INT_vect) -{ - // Enter only if any pinout pin is actively low. - if ((PINOUT_PIN & PINOUT_MASK) ^ PINOUT_MASK) { - if (bit_isfalse(PINOUT_PIN,bit(PIN_RESET))) { - mc_reset(); - } else if (bit_isfalse(PINOUT_PIN,bit(PIN_FEED_HOLD))) { - sys.execute |= EXEC_FEED_HOLD; - } else if (bit_isfalse(PINOUT_PIN,bit(PIN_CYCLE_START))) { - sys.execute |= EXEC_CYCLE_START; - } + } else if (line[0] == '$') { + // Grbl '$' system command + report_status_message(system_execute_line(line)); + + } else if (sys.state == STATE_ALARM) { + // Everything else is gcode. Block if in alarm mode. + report_status_message(STATUS_ALARM_LOCK); + + } else { + // Parse and execute g-code block! + report_status_message(gc_execute_line(line)); } } + +/* + GRBL PRIMARY LOOP: +*/ +void protocol_main_loop() +{ + // ------------------------------------------------------------ + // Complete initialization procedures upon a power-up or reset. + // ------------------------------------------------------------ + + // Print welcome message + report_init_message(); + + // Check for and report alarm state after a reset, error, or an initial power up. + if (sys.state == STATE_ALARM) { + report_feedback_message(MESSAGE_ALARM_LOCK); + } else { + // All systems go! + sys.state = STATE_IDLE; // Set system to ready. Clear all state flags. + system_execute_startup(line); // Execute startup script. + } + + // --------------------------------------------------------------------------------- + // Primary loop! Upon a system abort, this exits back to main() to reset the system. + // --------------------------------------------------------------------------------- + + uint8_t iscomment = false; + uint8_t char_counter = 0; + uint8_t c; + for (;;) { + + // Process one line of incoming serial data, as the data becomes available. Performs an + // initial filtering by removing spaces and comments and capitalizing all letters. + + // NOTE: While comment, spaces, and block delete(if supported) handling should technically + // be done in the g-code parser, doing it here helps compress the incoming data into Grbl's + // line buffer, which is limited in size. The g-code standard actually states a line can't + // exceed 256 characters, but the Arduino Uno does not have the memory space for this. + // With a better processor, it would be very easy to pull this initial parsing out as a + // seperate task to be shared by the g-code parser and Grbl's system commands. + + while((c = serial_read()) != SERIAL_NO_DATA) { + if ((c == '\n') || (c == '\r')) { // End of line reached + line[char_counter] = 0; // Set string termination character. + protocol_execute_line(line); // Line is complete. Execute it! + iscomment = false; + char_counter = 0; + } else { + if (iscomment) { + // Throw away all comment characters + if (c == ')') { + // End of comment. Resume line. + iscomment = false; + } + } else { + if (c <= ' ') { + // Throw away whitepace and control characters + } else if (c == '/') { + // Block delete NOT SUPPORTED. Ignore character. + // NOTE: If supported, would simply need to check the system if block delete is enabled. + } else if (c == '(') { + // Enable comments flag and ignore all characters until ')' or EOL. + // NOTE: This doesn't follow the NIST definition exactly, but is good enough for now. + // In the future, we could simply remove the items within the comments, but retain the + // comment control characters, so that the g-code parser can error-check it. + iscomment = true; + // } else if (c == ';') { + // Comment character to EOL NOT SUPPORTED. LinuxCNC definition. Not NIST. + + // TODO: Install '%' feature + // } else if (c == '%') { + // Program start-end percent sign NOT SUPPORTED. + // NOTE: This maybe installed to tell Grbl when a program is running vs manual input, + // where, during a program, the system auto-cycle start will continue to execute + // everything until the next '%' sign. This will help fix resuming issues with certain + // functions that empty the planner buffer to execute its task on-time. + + } else if (char_counter >= LINE_BUFFER_SIZE-1) { + // Detect line buffer overflow. Report error and reset line buffer. + report_status_message(STATUS_OVERFLOW); + iscomment = false; + char_counter = 0; + } else if (c >= 'a' && c <= 'z') { // Upcase lowercase + line[char_counter++] = c-'a'+'A'; + } else { + line[char_counter++] = c; + } + } + } + } + + // If there are no more characters in the serial read buffer to be processed and executed, + // this indicates that g-code streaming has either filled the planner buffer or has + // completed. In either case, auto-cycle start, if enabled, any queued moves. + protocol_auto_cycle_start(); + + protocol_execute_runtime(); // Runtime command check point. + if (sys.abort) { return; } // Bail to main() program loop to reset system. + + } + + return; /* Never reached */ +} + + // Executes run-time commands, when required. This is called from various check points in the main // program, primarily where there may be a while loop waiting for a buffer to clear space or any // point where the execution time from the last check point may be more than a fraction of a second. @@ -96,8 +177,8 @@ ISR(PINOUT_INT_vect) // limit switches, or the main program. void protocol_execute_runtime() { - if (sys.execute) { // Enter only if any bit flag is true - uint8_t rt_exec = sys.execute; // Avoid calling volatile multiple times + uint8_t rt_exec = sys.execute; // Copy to avoid calling volatile multiple times + if (rt_exec) { // Enter only if any bit flag is true // System alarm. Everything has shutdown by something that has gone severely wrong. Report // the source of the error to the user. If critical, Grbl disables by entering an infinite @@ -105,11 +186,13 @@ void protocol_execute_runtime() if (rt_exec & (EXEC_ALARM | EXEC_CRIT_EVENT)) { sys.state = STATE_ALARM; // Set system alarm state - // Critical event. Only hard/soft limit errors currently qualify. + // Critical events. Hard/soft limit events identified by both critical event and alarm exec + // flags. Probe fail is identified by the critical event exec flag only. if (rt_exec & EXEC_CRIT_EVENT) { - report_alarm_message(ALARM_LIMIT_ERROR); + if (rt_exec & EXEC_ALARM) { report_alarm_message(ALARM_LIMIT_ERROR); } + else { report_alarm_message(ALARM_PROBE_FAIL); } report_feedback_message(MESSAGE_CRITICAL_EVENT); - bit_false(sys.execute,EXEC_RESET); // Disable any existing reset + bit_false_atomic(sys.execute,EXEC_RESET); // Disable any existing reset do { // Nothing. Block EVERYTHING until user issues reset or power cycles. Hard limits // typically occur while unattended or not paying attention. Gives the user time @@ -125,7 +208,7 @@ void protocol_execute_runtime() // to indicate the possible severity of the problem. report_alarm_message(ALARM_ABORT_CYCLE); } - bit_false(sys.execute,(EXEC_ALARM | EXEC_CRIT_EVENT)); + bit_false_atomic(sys.execute,(EXEC_ALARM | EXEC_CRIT_EVENT)); } // Execute system abort. @@ -137,200 +220,80 @@ void protocol_execute_runtime() // Execute and serial print status if (rt_exec & EXEC_STATUS_REPORT) { report_realtime_status(); - bit_false(sys.execute,EXEC_STATUS_REPORT); + bit_false_atomic(sys.execute,EXEC_STATUS_REPORT); } - // Initiate stepper feed hold + // Execute a feed hold with deceleration, only during cycle. if (rt_exec & EXEC_FEED_HOLD) { - st_feed_hold(); // Initiate feed hold. - bit_false(sys.execute,EXEC_FEED_HOLD); + // !!! During a cycle, the segment buffer has just been reloaded and full. So the math involved + // with the feed hold should be fine for most, if not all, operational scenarios. + if (sys.state == STATE_CYCLE) { + sys.state = STATE_HOLD; + st_update_plan_block_parameters(); + st_prep_buffer(); + sys.auto_start = false; // Disable planner auto start upon feed hold. + } + bit_false_atomic(sys.execute,EXEC_FEED_HOLD); + } + + // Execute a cycle start by starting the stepper interrupt begin executing the blocks in queue. + if (rt_exec & EXEC_CYCLE_START) { + if (sys.state == STATE_QUEUED) { + sys.state = STATE_CYCLE; + st_prep_buffer(); // Initialize step segment buffer before beginning cycle. + st_wake_up(); + if (bit_istrue(settings.flags,BITFLAG_AUTO_START)) { + sys.auto_start = true; // Re-enable auto start after feed hold. + } else { + sys.auto_start = false; // Reset auto start per settings. + } + } + bit_false_atomic(sys.execute,EXEC_CYCLE_START); } - // Reinitializes the stepper module running state and, if a feed hold, re-plans the buffer. + // Reinitializes the cycle plan and stepper system after a feed hold for a resume. Called by + // runtime command execution in the main program, ensuring that the planner re-plans safely. + // NOTE: Bresenham algorithm variables are still maintained through both the planner and stepper + // cycle reinitializations. The stepper path should continue exactly as if nothing has happened. // NOTE: EXEC_CYCLE_STOP is set by the stepper subsystem when a cycle or feed hold completes. if (rt_exec & EXEC_CYCLE_STOP) { - st_cycle_reinitialize(); - bit_false(sys.execute,EXEC_CYCLE_STOP); - } - - if (rt_exec & EXEC_CYCLE_START) { - st_cycle_start(); // Issue cycle start command to stepper subsystem - if (bit_istrue(settings.flags,BITFLAG_AUTO_START)) { - sys.auto_start = true; // Re-enable auto start after feed hold. - } - bit_false(sys.execute,EXEC_CYCLE_START); + if ( plan_get_current_block() ) { sys.state = STATE_QUEUED; } + else { sys.state = STATE_IDLE; } + bit_false_atomic(sys.execute,EXEC_CYCLE_STOP); } + } // Overrides flag byte (sys.override) and execution should be installed here, since they // are runtime and require a direct and controlled interface to the main stepper program. + + // Reload step segment buffer + if (sys.state & (STATE_CYCLE | STATE_HOLD | STATE_HOMING)) { st_prep_buffer(); } + } -// Directs and executes one line of formatted input from protocol_process. While mostly -// incoming streaming g-code blocks, this also executes Grbl internal commands, such as -// settings, initiating the homing cycle, and toggling switch states. This differs from -// the runtime command module by being susceptible to when Grbl is ready to execute the -// next line during a cycle, so for switches like block delete, the switch only effects -// the lines that are processed afterward, not necessarily real-time during a cycle, -// since there are motions already stored in the buffer. However, this 'lag' should not -// be an issue, since these commands are not typically used during a cycle. -uint8_t protocol_execute_line(char *line) -{ - // Grbl internal command and parameter lines are of the form '$4=374.3' or '$' for help - if(line[0] == '$') { - - uint8_t char_counter = 1; - uint8_t helper_var = 0; // Helper variable - float parameter, value; - switch( line[char_counter] ) { - case 0 : report_grbl_help(); break; - case '$' : // Prints Grbl settings - if ( line[++char_counter] != 0 ) { return(STATUS_UNSUPPORTED_STATEMENT); } - else { report_grbl_settings(); } - break; - case '#' : // Print gcode parameters - if ( line[++char_counter] != 0 ) { return(STATUS_UNSUPPORTED_STATEMENT); } - else { report_gcode_parameters(); } - break; - case 'G' : // Prints gcode parser state - if ( line[++char_counter] != 0 ) { return(STATUS_UNSUPPORTED_STATEMENT); } - else { report_gcode_modes(); } - break; - case 'C' : // Set check g-code mode - if ( line[++char_counter] != 0 ) { return(STATUS_UNSUPPORTED_STATEMENT); } - // Perform reset when toggling off. Check g-code mode should only work if Grbl - // is idle and ready, regardless of alarm locks. This is mainly to keep things - // simple and consistent. - if ( sys.state == STATE_CHECK_MODE ) { - mc_reset(); - report_feedback_message(MESSAGE_DISABLED); - } else { - if (sys.state) { return(STATUS_IDLE_ERROR); } - sys.state = STATE_CHECK_MODE; - report_feedback_message(MESSAGE_ENABLED); - } - break; - case 'X' : // Disable alarm lock - if ( line[++char_counter] != 0 ) { return(STATUS_UNSUPPORTED_STATEMENT); } - if (sys.state == STATE_ALARM) { - report_feedback_message(MESSAGE_ALARM_UNLOCK); - sys.state = STATE_IDLE; - // Don't run startup script. Prevents stored moves in startup from causing accidents. - } - break; - case 'H' : // Perform homing cycle - if (bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) { - // Only perform homing if Grbl is idle or lost. - if ( sys.state==STATE_IDLE || sys.state==STATE_ALARM ) { - mc_go_home(); - if (!sys.abort) { protocol_execute_startup(); } // Execute startup scripts after successful homing. - } else { return(STATUS_IDLE_ERROR); } - } else { return(STATUS_SETTING_DISABLED); } - break; -// case 'J' : break; // Jogging methods - // TODO: Here jogging can be placed for execution as a seperate subprogram. It does not need to be - // susceptible to other runtime commands except for e-stop. The jogging function is intended to - // be a basic toggle on/off with controlled acceleration and deceleration to prevent skipped - // steps. The user would supply the desired feedrate, axis to move, and direction. Toggle on would - // start motion and toggle off would initiate a deceleration to stop. One could 'feather' the - // motion by repeatedly toggling to slow the motion to the desired location. Location data would - // need to be updated real-time and supplied to the user through status queries. - // More controlled exact motions can be taken care of by inputting G0 or G1 commands, which are - // handled by the planner. It would be possible for the jog subprogram to insert blocks into the - // block buffer without having the planner plan them. It would need to manage de/ac-celerations - // on its own carefully. This approach could be effective and possibly size/memory efficient. - case 'N' : // Startup lines. - if ( line[++char_counter] == 0 ) { // Print startup lines - for (helper_var=0; helper_var < N_STARTUP_LINE; helper_var++) { - if (!(settings_read_startup_line(helper_var, line))) { - report_status_message(STATUS_SETTING_READ_FAIL); - } else { - report_startup_line(helper_var,line); - } - } - break; - } else { // Store startup line - helper_var = true; // Set helper_var to flag storing method. - // No break. Continues into default: to read remaining command characters. - } - default : // Storing setting methods - if(!read_float(line, &char_counter, ¶meter)) { return(STATUS_BAD_NUMBER_FORMAT); } - if(line[char_counter++] != '=') { return(STATUS_UNSUPPORTED_STATEMENT); } - if (helper_var) { // Store startup line - // Prepare sending gcode block to gcode parser by shifting all characters - helper_var = char_counter; // Set helper variable as counter to start of gcode block - do { - line[char_counter-helper_var] = line[char_counter]; - } while (line[char_counter++] != 0); - // Execute gcode block to ensure block is valid. - helper_var = gc_execute_line(line); // Set helper_var to returned status code. - if (helper_var) { return(helper_var); } - else { - helper_var = trunc(parameter); // Set helper_var to int value of parameter - settings_store_startup_line(helper_var,line); - } - } else { // Store global setting. - if(!read_float(line, &char_counter, &value)) { return(STATUS_BAD_NUMBER_FORMAT); } - if(line[char_counter] != 0) { return(STATUS_UNSUPPORTED_STATEMENT); } - return(settings_store_global_setting(parameter, value)); - } - } - return(STATUS_OK); // If '$' command makes it to here, then everything's ok. - - } else { - return(gc_execute_line(line)); // Everything else is gcode - } -} - - -// Process and report status one line of incoming serial data. Performs an initial filtering -// by removing spaces and comments and capitalizing all letters. -void protocol_process() +// Block until all buffered steps are executed or in a cycle state. Works with feed hold +// during a synchronize call, if it should happen. Also, waits for clean cycle end. +void protocol_buffer_synchronize() { - uint8_t c; - while((c = serial_read()) != SERIAL_NO_DATA) { - if ((c == '\n') || (c == '\r')) { // End of line reached - - // Runtime command check point before executing line. Prevent any furthur line executions. - // NOTE: If there is no line, this function should quickly return to the main program when - // the buffer empties of non-executable data. - protocol_execute_runtime(); - if (sys.abort) { return; } // Bail to main program upon system abort - - if (char_counter > 0) {// Line is complete. Then execute! - line[char_counter] = 0; // Terminate string - report_status_message(protocol_execute_line(line)); - } else { - // Empty or comment line. Skip block. - report_status_message(STATUS_OK); // Send status message for syncing purposes. - } - char_counter = 0; // Reset line buffer index - iscomment = false; // Reset comment flag - - } else { - if (iscomment) { - // Throw away all comment characters - if (c == ')') { - // End of comment. Resume line. - iscomment = false; - } - } else { - if (c <= ' ') { - // Throw away whitepace and control characters - } else if (c == '/') { - // Block delete not supported. Ignore character. - } else if (c == '(') { - // Enable comments flag and ignore all characters until ')' or EOL. - iscomment = true; - } else if (char_counter >= LINE_BUFFER_SIZE-1) { - // Throw away any characters beyond the end of the line buffer - } else if (c >= 'a' && c <= 'z') { // Upcase lowercase - line[char_counter++] = c-'a'+'A'; - } else { - line[char_counter++] = c; - } - } - } - } + // Check and set auto start to resume cycle after synchronize and caller completes. + if (sys.state == STATE_CYCLE) { sys.auto_start = true; } + while (plan_get_current_block() || (sys.state == STATE_CYCLE)) { + protocol_execute_runtime(); // Check and execute run-time commands + if (sys.abort) { return; } // Check for system abort + } } + + +// Auto-cycle start has two purposes: 1. Resumes a plan_synchronize() call from a function that +// requires the planner buffer to empty (spindle enable, dwell, etc.) 2. As a user setting that +// automatically begins the cycle when a user enters a valid motion command manually. This is +// intended as a beginners feature to help new users to understand g-code. It can be disabled +// as a beginner tool, but (1.) still operates. If disabled, the operation of cycle start is +// manually issuing a cycle start command whenever the user is ready and there is a valid motion +// command in the planner queue. +// NOTE: This function is called from the main loop and mc_line() only and executes when one of +// two conditions exist respectively: There are no more blocks sent (i.e. streaming is finished, +// single commands), or the planner buffer is full and ready to go. +void protocol_auto_cycle_start() { if (sys.auto_start) { bit_true_atomic(sys.execute, EXEC_CYCLE_START); } } diff --git a/protocol.h b/protocol.h index 209d19d..7bafba2 100644 --- a/protocol.h +++ b/protocol.h @@ -1,9 +1,9 @@ /* - protocol.h - the serial protocol master control unit + protocol.h - controls Grbl execution protocol and procedures Part of Grbl + Copyright (c) 2011-2014 Sungeun K. Jeon Copyright (c) 2009-2011 Simen Svale Skogsrud - Copyright (c) 2011-2012 Sungeun K. Jeon Grbl is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -21,32 +21,36 @@ #ifndef protocol_h #define protocol_h -#include - // Line buffer size from the serial input stream to be executed. // NOTE: Not a problem except for extreme cases, but the line buffer size can be too small // and g-code blocks can get truncated. Officially, the g-code standards support up to 256 // characters. In future versions, this will be increased, when we know how much extra -// memory space we can invest into here or we re-write the g-code parser not to have his +// memory space we can invest into here or we re-write the g-code parser not to have this // buffer. #ifndef LINE_BUFFER_SIZE - #define LINE_BUFFER_SIZE 50 + #define LINE_BUFFER_SIZE 70 #endif -// Initialize the serial protocol -void protocol_init(); - -// Read command lines from the serial port and execute them as they -// come in. Blocks until the serial buffer is emptied. -void protocol_process(); - -// Executes one line of input according to protocol -uint8_t protocol_execute_line(char *line); +// Starts Grbl main loop. It handles all incoming characters from the serial port and executes +// them as they complete. It is also responsible for finishing the initialization procedures. +void protocol_main_loop(); // Checks and executes a runtime command at various stop points in main program void protocol_execute_runtime(); -// Execute the startup script lines stored in EEPROM upon initialization -void protocol_execute_startup(); +// Notify the stepper subsystem to start executing the g-code program in buffer. +// void protocol_cycle_start(); + +// Reinitializes the buffer after a feed hold for a resume. +// void protocol_cycle_reinitialize(); + +// Initiates a feed hold of the running program +// void protocol_feed_hold(); + +// Executes the auto cycle feature, if enabled. +void protocol_auto_cycle_start(); + +// Block until all buffered steps are executed +void protocol_buffer_synchronize(); #endif diff --git a/report.c b/report.c index c115dc7..214974d 100644 --- a/report.c +++ b/report.c @@ -2,7 +2,7 @@ report.c - reporting and messaging methods Part of Grbl - Copyright (c) 2012-2013 Sungeun K. Jeon + Copyright (c) 2012-2014 Sungeun K. Jeon Grbl is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -26,13 +26,15 @@ methods to accomodate their needs. */ -#include +#include "system.h" #include "report.h" #include "print.h" #include "settings.h" -#include "nuts_bolts.h" #include "gcode.h" #include "coolant_control.h" +#include "planner.h" +#include "spindle_control.h" +#include "stepper.h" // Handles the primary confirmation protocol response for streaming interfaces and human-feedback. @@ -50,32 +52,40 @@ void report_status_message(uint8_t status_code) } else { printPgmString(PSTR("error: ")); switch(status_code) { - case STATUS_BAD_NUMBER_FORMAT: - printPgmString(PSTR("Bad number format")); break; case STATUS_EXPECTED_COMMAND_LETTER: printPgmString(PSTR("Expected command letter")); break; - case STATUS_UNSUPPORTED_STATEMENT: - printPgmString(PSTR("Unsupported statement")); break; - case STATUS_ARC_RADIUS_ERROR: - printPgmString(PSTR("Invalid radius")); break; - case STATUS_MODAL_GROUP_VIOLATION: - printPgmString(PSTR("Modal group violation")); break; + case STATUS_BAD_NUMBER_FORMAT: + printPgmString(PSTR("Bad number format")); break; case STATUS_INVALID_STATEMENT: printPgmString(PSTR("Invalid statement")); break; + case STATUS_NEGATIVE_VALUE: + printPgmString(PSTR("Value < 0")); break; case STATUS_SETTING_DISABLED: printPgmString(PSTR("Setting disabled")); break; - case STATUS_SETTING_VALUE_NEG: - printPgmString(PSTR("Value < 0.0")); break; case STATUS_SETTING_STEP_PULSE_MIN: printPgmString(PSTR("Value < 3 usec")); break; case STATUS_SETTING_READ_FAIL: printPgmString(PSTR("EEPROM read fail. Using defaults")); break; case STATUS_IDLE_ERROR: - printPgmString(PSTR("Busy or queued")); break; + printPgmString(PSTR("Not idle")); break; case STATUS_ALARM_LOCK: printPgmString(PSTR("Alarm lock")); break; case STATUS_SOFT_LIMIT_ERROR: printPgmString(PSTR("Homing not enabled")); break; + case STATUS_OVERFLOW: + printPgmString(PSTR("Line overflow")); break; + + // Common g-code parser errors. + case STATUS_GCODE_MODAL_GROUP_VIOLATION: + printPgmString(PSTR("Modal group violation")); break; + case STATUS_GCODE_UNSUPPORTED_COMMAND: + printPgmString(PSTR("Unsupported command")); break; + case STATUS_GCODE_UNDEFINED_FEED_RATE: + printPgmString(PSTR("Undefined feed rate")); break; + default: + // Remaining g-code parser errors with error codes + printPgmString(PSTR("Invalid gcode ID:")); + print_uint8_base10(status_code); // Print error code for user reference } printPgmString(PSTR("\r\n")); } @@ -90,8 +100,10 @@ void report_alarm_message(int8_t alarm_code) printPgmString(PSTR("Hard/soft limit")); break; case ALARM_ABORT_CYCLE: printPgmString(PSTR("Abort during cycle")); break; + case ALARM_PROBE_FAIL: + printPgmString(PSTR("Probe fail")); break; } - printPgmString(PSTR(". MPos?\r\n")); + printPgmString(PSTR("\r\n")); delay_ms(500); // Force delay to ensure message clears serial write buffer. } @@ -146,44 +158,64 @@ void report_grbl_help() { // Grbl global settings print out. // NOTE: The numbering scheme here must correlate to storing in settings.c void report_grbl_settings() { - printPgmString(PSTR("$0=")); printFloat(settings.steps_per_mm[X_AXIS]); - printPgmString(PSTR(" (x, step/mm)\r\n$1=")); printFloat(settings.steps_per_mm[Y_AXIS]); - printPgmString(PSTR(" (y, step/mm)\r\n$2=")); printFloat(settings.steps_per_mm[Z_AXIS]); - printPgmString(PSTR(" (z, step/mm)\r\n$3=")); printFloat(settings.max_velocity[X_AXIS]); - printPgmString(PSTR(" (x v_max, mm/min)\r\n$4=")); printFloat(settings.max_velocity[Y_AXIS]); - printPgmString(PSTR(" (y v_max, mm/min)\r\n$5=")); printFloat(settings.max_velocity[Z_AXIS]); - printPgmString(PSTR(" (z v_max, mm/min)\r\n$6=")); printFloat(settings.acceleration[X_AXIS]/(60*60)); // Convert from mm/min^2 for human readability - printPgmString(PSTR(" (x accel, mm/sec^2)\r\n$7=")); printFloat(settings.acceleration[Y_AXIS]/(60*60)); // Convert from mm/min^2 for human readability - printPgmString(PSTR(" (y accel, mm/sec^2)\r\n$8=")); printFloat(settings.acceleration[Z_AXIS]/(60*60)); // Convert from mm/min^2 for human readability - printPgmString(PSTR(" (z accel, mm/sec^2)\r\n$9=")); printFloat(settings.max_travel[X_AXIS]); - printPgmString(PSTR(" (x max travel, mm)\r\n$10=")); printFloat(settings.max_travel[Y_AXIS]); - printPgmString(PSTR(" (y max travel, mm)\r\n$11=")); printFloat(settings.max_travel[Z_AXIS]); - printPgmString(PSTR(" (z max travel, mm)\r\n$12=")); printInteger(settings.pulse_microseconds); - printPgmString(PSTR(" (step pulse, usec)\r\n$13=")); printFloat(settings.default_feed_rate); - printPgmString(PSTR(" (default feed, mm/min)\r\n$14=")); printInteger(settings.invert_mask); - printPgmString(PSTR(" (step port invert mask, int:")); print_uint8_base2(settings.invert_mask); - printPgmString(PSTR(")\r\n$15=")); printInteger(settings.stepper_idle_lock_time); - printPgmString(PSTR(" (step idle delay, msec)\r\n$16=")); printFloat(settings.junction_deviation); - printPgmString(PSTR(" (junction deviation, mm)\r\n$17=")); printFloat(settings.arc_tolerance); - printPgmString(PSTR(" (arc tolerance, mm)\r\n$18=")); printInteger(settings.decimal_places); - printPgmString(PSTR(" (n-decimals, int)\r\n$19=")); printInteger(bit_istrue(settings.flags,BITFLAG_REPORT_INCHES)); - printPgmString(PSTR(" (report inches, bool)\r\n$20=")); printInteger(bit_istrue(settings.flags,BITFLAG_AUTO_START)); - printPgmString(PSTR(" (auto start, bool)\r\n$21=")); printInteger(bit_istrue(settings.flags,BITFLAG_INVERT_ST_ENABLE)); - printPgmString(PSTR(" (invert step enable, bool)\r\n$22=")); printInteger(bit_istrue(settings.flags,BITFLAG_SOFT_LIMIT_ENABLE)); - printPgmString(PSTR(" (soft limits, bool)\r\n$23=")); printInteger(bit_istrue(settings.flags,BITFLAG_HARD_LIMIT_ENABLE)); - printPgmString(PSTR(" (hard limits, bool)\r\n$24=")); printInteger(bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)); - printPgmString(PSTR(" (homing cycle, bool)\r\n$25=")); printInteger(settings.homing_dir_mask); - printPgmString(PSTR(" (homing dir invert mask, int:")); print_uint8_base2(settings.homing_dir_mask); - printPgmString(PSTR(")\r\n$26=")); printFloat(settings.homing_feed_rate); - printPgmString(PSTR(" (homing feed, mm/min)\r\n$27=")); printFloat(settings.homing_seek_rate); - printPgmString(PSTR(" (homing seek, mm/min)\r\n$28=")); printInteger(settings.homing_debounce_delay); - printPgmString(PSTR(" (homing debounce, msec)\r\n$29=")); printFloat(settings.homing_pulloff); + printPgmString(PSTR("$0=")); printFloat_SettingValue(settings.steps_per_mm[X_AXIS]); + printPgmString(PSTR(" (x, step/mm)\r\n$1=")); printFloat_SettingValue(settings.steps_per_mm[Y_AXIS]); + printPgmString(PSTR(" (y, step/mm)\r\n$2=")); printFloat_SettingValue(settings.steps_per_mm[Z_AXIS]); + printPgmString(PSTR(" (z, step/mm)\r\n$3=")); printFloat_SettingValue(settings.max_rate[X_AXIS]); + printPgmString(PSTR(" (x max rate, mm/min)\r\n$4=")); printFloat_SettingValue(settings.max_rate[Y_AXIS]); + printPgmString(PSTR(" (y max rate, mm/min)\r\n$5=")); printFloat_SettingValue(settings.max_rate[Z_AXIS]); + printPgmString(PSTR(" (z max rate, mm/min)\r\n$6=")); printFloat_SettingValue(settings.acceleration[X_AXIS]/(60*60)); // Convert from mm/min^2 for human readability + printPgmString(PSTR(" (x accel, mm/sec^2)\r\n$7=")); printFloat_SettingValue(settings.acceleration[Y_AXIS]/(60*60)); // Convert from mm/min^2 for human readability + printPgmString(PSTR(" (y accel, mm/sec^2)\r\n$8=")); printFloat_SettingValue(settings.acceleration[Z_AXIS]/(60*60)); // Convert from mm/min^2 for human readability + printPgmString(PSTR(" (z accel, mm/sec^2)\r\n$9=")); printFloat_SettingValue(-settings.max_travel[X_AXIS]); // Grbl internally store this as negative. + printPgmString(PSTR(" (x max travel, mm)\r\n$10=")); printFloat_SettingValue(-settings.max_travel[Y_AXIS]); // Grbl internally store this as negative. + printPgmString(PSTR(" (y max travel, mm)\r\n$11=")); printFloat_SettingValue(-settings.max_travel[Z_AXIS]); // Grbl internally store this as negative. + printPgmString(PSTR(" (z max travel, mm)\r\n$12=")); print_uint8_base10(settings.pulse_microseconds); + printPgmString(PSTR(" (step pulse, usec)\r\n$13=")); print_uint8_base10(settings.step_invert_mask); + printPgmString(PSTR(" (step port invert mask:")); print_uint8_base2(settings.step_invert_mask); + printPgmString(PSTR(")\r\n$14=")); print_uint8_base10(settings.dir_invert_mask); + printPgmString(PSTR(" (dir port invert mask:")); print_uint8_base2(settings.dir_invert_mask); + printPgmString(PSTR(")\r\n$15=")); print_uint8_base10(settings.stepper_idle_lock_time); + printPgmString(PSTR(" (step idle delay, msec)\r\n$16=")); printFloat_SettingValue(settings.junction_deviation); + printPgmString(PSTR(" (junction deviation, mm)\r\n$17=")); printFloat_SettingValue(settings.arc_tolerance); + printPgmString(PSTR(" (arc tolerance, mm)\r\n$19=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_REPORT_INCHES)); + printPgmString(PSTR(" (report inches, bool)\r\n$20=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_AUTO_START)); + printPgmString(PSTR(" (auto start, bool)\r\n$21=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_INVERT_ST_ENABLE)); + printPgmString(PSTR(" (invert step enable, bool)\r\n$22=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_INVERT_LIMIT_PINS)); + printPgmString(PSTR(" (invert limit pins, bool)\r\n$23=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_SOFT_LIMIT_ENABLE)); + printPgmString(PSTR(" (soft limits, bool)\r\n$24=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_HARD_LIMIT_ENABLE)); + printPgmString(PSTR(" (hard limits, bool)\r\n$25=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)); + printPgmString(PSTR(" (homing cycle, bool)\r\n$26=")); print_uint8_base10(settings.homing_dir_mask); + printPgmString(PSTR(" (homing dir invert mask:")); print_uint8_base2(settings.homing_dir_mask); + printPgmString(PSTR(")\r\n$27=")); printFloat_SettingValue(settings.homing_feed_rate); + printPgmString(PSTR(" (homing feed, mm/min)\r\n$28=")); printFloat_SettingValue(settings.homing_seek_rate); + printPgmString(PSTR(" (homing seek, mm/min)\r\n$29=")); print_uint8_base10(settings.homing_debounce_delay); + printPgmString(PSTR(" (homing debounce, msec)\r\n$30=")); printFloat_SettingValue(settings.homing_pulloff); printPgmString(PSTR(" (homing pull-off, mm)\r\n")); } -// Prints gcode coordinate offset parameters -void report_gcode_parameters() +// Prints current probe parameters. Upon a probe command, these parameters are updated upon a +// successful probe or upon a failed probe with the G38.3 without errors command (if supported). +// These values are retained until Grbl is power-cycled, whereby they will be re-zeroed. +void report_probe_parameters() +{ + uint8_t i; + float print_position[N_AXIS]; + + // Report in terms of machine position. + printPgmString(PSTR("[PRB:")); + for (i=0; i< N_AXIS; i++) { + print_position[i] = sys.probe_position[i]/settings.steps_per_mm[i]; + printFloat_CoordValue(print_position[i]); + if (i < (N_AXIS-1)) { printPgmString(PSTR(",")); } + } + printPgmString(PSTR("]\r\n")); +} + + +// Prints Grbl NGC parameters (coordinate offsets, probing) +void report_ngc_parameters() { float coord_data[N_AXIS]; uint8_t coord_select, i; @@ -194,74 +226,72 @@ void report_gcode_parameters() } printPgmString(PSTR("[G")); switch (coord_select) { - case 0: printPgmString(PSTR("54:")); break; - case 1: printPgmString(PSTR("55:")); break; - case 2: printPgmString(PSTR("56:")); break; - case 3: printPgmString(PSTR("57:")); break; - case 4: printPgmString(PSTR("58:")); break; - case 5: printPgmString(PSTR("59:")); break; - case 6: printPgmString(PSTR("28:")); break; - case 7: printPgmString(PSTR("30:")); break; - // case 8: printPgmString(PSTR("92:")); break; // G92.2, G92.3 not supported. Hence not stored. - } + case 6: printPgmString(PSTR("28")); break; + case 7: printPgmString(PSTR("30")); break; + default: print_uint8_base10(coord_select+54); break; // G54-G59 + } + printPgmString(PSTR(":")); for (i=0; iline_number; + } + printInteger(ln); + #endif + + #ifdef REPORT_REALTIME_RATE + // Report realtime rate + printPgmString(PSTR(",F:")); + printFloat_RateValue(st_get_realtime_rate()); + #endif + printPgmString(PSTR(">\r\n")); } diff --git a/report.h b/report.h index cd7f42d..4f1d768 100644 --- a/report.h +++ b/report.h @@ -2,7 +2,7 @@ report.h - reporting and messaging methods Part of Grbl - Copyright (c) 2012-2013 Sungeun K. Jeon + Copyright (c) 2012-2014 Sungeun K. Jeon Grbl is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -20,26 +20,44 @@ #ifndef report_h #define report_h - // Define Grbl status codes. #define STATUS_OK 0 -#define STATUS_BAD_NUMBER_FORMAT 1 -#define STATUS_EXPECTED_COMMAND_LETTER 2 -#define STATUS_UNSUPPORTED_STATEMENT 3 -#define STATUS_ARC_RADIUS_ERROR 4 -#define STATUS_MODAL_GROUP_VIOLATION 5 -#define STATUS_INVALID_STATEMENT 6 -#define STATUS_SETTING_DISABLED 7 -#define STATUS_SETTING_VALUE_NEG 8 -#define STATUS_SETTING_STEP_PULSE_MIN 9 -#define STATUS_SETTING_READ_FAIL 10 -#define STATUS_IDLE_ERROR 11 -#define STATUS_ALARM_LOCK 12 -#define STATUS_SOFT_LIMIT_ERROR 13 +#define STATUS_EXPECTED_COMMAND_LETTER 1 +#define STATUS_BAD_NUMBER_FORMAT 2 +#define STATUS_INVALID_STATEMENT 3 +#define STATUS_NEGATIVE_VALUE 4 +#define STATUS_SETTING_DISABLED 5 +#define STATUS_SETTING_STEP_PULSE_MIN 6 +#define STATUS_SETTING_READ_FAIL 7 +#define STATUS_IDLE_ERROR 8 +#define STATUS_ALARM_LOCK 9 +#define STATUS_SOFT_LIMIT_ERROR 10 +#define STATUS_OVERFLOW 11 + +#define STATUS_GCODE_UNSUPPORTED_COMMAND 20 +#define STATUS_GCODE_MODAL_GROUP_VIOLATION 21 +#define STATUS_GCODE_UNDEFINED_FEED_RATE 22 +#define STATUS_GCODE_COMMAND_VALUE_NOT_INTEGER 23 +#define STATUS_GCODE_AXIS_COMMAND_CONFLICT 24 +#define STATUS_GCODE_WORD_REPEATED 25 +#define STATUS_GCODE_NO_AXIS_WORDS 26 +#define STATUS_GCODE_INVALID_LINE_NUMBER 27 +#define STATUS_GCODE_VALUE_WORD_MISSING 28 +#define STATUS_GCODE_UNSUPPORTED_COORD_SYS 29 +#define STATUS_GCODE_G53_INVALID_MOTION_MODE 30 +#define STATUS_GCODE_AXIS_WORDS_EXIST 31 +#define STATUS_GCODE_NO_AXIS_WORDS_IN_PLANE 32 +#define STATUS_GCODE_INVALID_TARGET 33 +#define STATUS_GCODE_ARC_RADIUS_ERROR 34 +#define STATUS_GCODE_NO_OFFSETS_IN_PLANE 35 +#define STATUS_GCODE_PROBE_TRIGGERED 36 +#define STATUS_GCODE_UNUSED_WORDS 37 +#define STATUS_GCODE_G43_DYNAMIC_AXIS_ERROR 38 // Define Grbl alarm codes. Less than zero to distinguish alarm error from status error. #define ALARM_LIMIT_ERROR -1 #define ALARM_ABORT_CYCLE -2 +#define ALARM_PROBE_FAIL -3 // Define Grbl feedback message codes. #define MESSAGE_CRITICAL_EVENT 1 @@ -69,8 +87,11 @@ void report_grbl_settings(); // Prints realtime status report void report_realtime_status(); -// Prints Grbl persistent coordinate parameters -void report_gcode_parameters(); +// Prints recorded probe position +void report_probe_parameters(); + +// Prints Grbl NGC parameters (coordinate offsets, probe) +void report_ngc_parameters(); // Prints current g-code parser mode state void report_gcode_modes(); @@ -78,4 +99,7 @@ void report_gcode_modes(); // Prints startup line void report_startup_line(uint8_t n, char *line); +// Prints build info and user info +void report_build_info(char *line); + #endif diff --git a/serial.c b/serial.c index 69fa717..9f352f5 100644 --- a/serial.c +++ b/serial.c @@ -2,8 +2,8 @@ serial.c - Low level functions for sending and recieving bytes via the serial port Part of Grbl + Copyright (c) 2011-2014 Sungeun K. Jeon Copyright (c) 2009-2011 Simen Svale Skogsrud - Copyright (c) 2011-2012 Sungeun K. Jeon Grbl is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -23,19 +23,21 @@ used to be a part of the Arduino project. */ #include +#include "system.h" #include "serial.h" -#include "config.h" #include "motion_control.h" #include "protocol.h" + uint8_t rx_buffer[RX_BUFFER_SIZE]; uint8_t rx_buffer_head = 0; -uint8_t rx_buffer_tail = 0; +volatile uint8_t rx_buffer_tail = 0; uint8_t tx_buffer[TX_BUFFER_SIZE]; uint8_t tx_buffer_head = 0; volatile uint8_t tx_buffer_tail = 0; + #ifdef ENABLE_XONXOFF volatile uint8_t flow_ctrl = XON_SENT; // Flow control state variable @@ -49,6 +51,7 @@ volatile uint8_t tx_buffer_tail = 0; } #endif + void serial_init() { // Set baud rate @@ -72,6 +75,7 @@ void serial_init() // defaults to 8-bit, no parity, 1 stop bit } + void serial_write(uint8_t data) { // Calculate next head uint8_t next_head = tx_buffer_head + 1; @@ -90,15 +94,11 @@ void serial_write(uint8_t data) { UCSR0B |= (1 << UDRIE0); } + // Data Register Empty Interrupt handler -#ifdef __AVR_ATmega644P__ -ISR(USART0_UDRE_vect) -#else -ISR(USART_UDRE_vect) -#endif +ISR(SERIAL_UDRE) { - // Temporary tx_buffer_tail (to optimize for volatile) - uint8_t tail = tx_buffer_tail; + uint8_t tail = tx_buffer_tail; // Temporary tx_buffer_tail (to optimize for volatile) #ifdef ENABLE_XONXOFF if (flow_ctrl == SEND_XOFF) { @@ -124,14 +124,18 @@ ISR(USART_UDRE_vect) if (tail == tx_buffer_head) { UCSR0B &= ~(1 << UDRIE0); } } + uint8_t serial_read() { - if (rx_buffer_head == rx_buffer_tail) { + uint8_t tail = rx_buffer_tail; // Temporary rx_buffer_tail (to optimize for volatile) + if (rx_buffer_head == tail) { return SERIAL_NO_DATA; } else { - uint8_t data = rx_buffer[rx_buffer_tail]; - rx_buffer_tail++; - if (rx_buffer_tail == RX_BUFFER_SIZE) { rx_buffer_tail = 0; } + uint8_t data = rx_buffer[tail]; + + tail++; + if (tail == RX_BUFFER_SIZE) { tail = 0; } + rx_buffer_tail = tail; #ifdef ENABLE_XONXOFF if ((get_rx_buffer_count() < RX_BUFFER_LOW) && flow_ctrl == XOFF_SENT) { @@ -144,11 +148,8 @@ uint8_t serial_read() } } -#ifdef __AVR_ATmega644P__ -ISR(USART0_RX_vect) -#else -ISR(USART_RX_vect) -#endif + +ISR(SERIAL_RX) { uint8_t data = UDR0; uint8_t next_head; @@ -156,9 +157,9 @@ ISR(USART_RX_vect) // Pick off runtime command characters directly from the serial stream. These characters are // not passed into the buffer, but these set system state flag bits for runtime execution. switch (data) { - case CMD_STATUS_REPORT: sys.execute |= EXEC_STATUS_REPORT; break; // Set as true - case CMD_CYCLE_START: sys.execute |= EXEC_CYCLE_START; break; // Set as true - case CMD_FEED_HOLD: sys.execute |= EXEC_FEED_HOLD; break; // Set as true + case CMD_STATUS_REPORT: bit_true_atomic(sys.execute, EXEC_STATUS_REPORT); break; // Set as true + case CMD_CYCLE_START: bit_true_atomic(sys.execute, EXEC_CYCLE_START); break; // Set as true + case CMD_FEED_HOLD: bit_true_atomic(sys.execute, EXEC_FEED_HOLD); break; // Set as true case CMD_RESET: mc_reset(); break; // Call motion control reset routine. default: // Write character to buffer next_head = rx_buffer_head + 1; @@ -177,9 +178,11 @@ ISR(USART_RX_vect) #endif } + //TODO: else alarm on overflow? } } + void serial_reset_read_buffer() { rx_buffer_tail = rx_buffer_head; diff --git a/serial.h b/serial.h index 723d48f..74b429b 100644 --- a/serial.h +++ b/serial.h @@ -2,8 +2,8 @@ serial.c - Low level functions for sending and recieving bytes via the serial port Part of Grbl + Copyright (c) 2011-2014 Sungeun K. Jeon Copyright (c) 2009-2011 Simen Svale Skogsrud - Copyright (c) 2011-2012 Sungeun K. Jeon Grbl is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -25,7 +25,6 @@ #ifndef serial_h #define serial_h -#include "nuts_bolts.h" #ifndef RX_BUFFER_SIZE #define RX_BUFFER_SIZE 128 diff --git a/settings.c b/settings.c index 9225a91..88df8ef 100644 --- a/settings.c +++ b/settings.c @@ -2,8 +2,8 @@ settings.c - eeprom configuration handling Part of Grbl + Copyright (c) 2011-2014 Sungeun K. Jeon Copyright (c) 2009-2011 Simen Svale Skogsrud - Copyright (c) 2011-2013 Sungeun K. Jeon Grbl is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -19,29 +19,15 @@ along with Grbl. If not, see . */ -#include -#include "protocol.h" -#include "report.h" -#include "stepper.h" -#include "nuts_bolts.h" +#include "system.h" #include "settings.h" #include "eeprom.h" +#include "protocol.h" +#include "report.h" #include "limits.h" settings_t settings; -// Version 4 outdated settings record -typedef struct { - float steps_per_mm[N_AXIS]; - uint8_t microsteps; - uint8_t pulse_microseconds; - float default_feed_rate; - uint8_t invert_mask; - float mm_per_arc_segment; - float acceleration; - float junction_deviation; -} settings_v4_t; - // Method to store startup lines into EEPROM void settings_store_startup_line(uint8_t n, char *line) @@ -50,6 +36,14 @@ void settings_store_startup_line(uint8_t n, char *line) memcpy_to_eeprom_with_checksum(addr,(char*)line, LINE_BUFFER_SIZE); } + +// Method to store build info into EEPROM +void settings_store_build_info(char *line) +{ + memcpy_to_eeprom_with_checksum(EEPROM_ADDR_BUILD_INFO,(char*)line, LINE_BUFFER_SIZE); +} + + // Method to store coord data parameters into EEPROM void settings_write_coord_data(uint8_t coord_select, float *coord_data) { @@ -57,6 +51,7 @@ void settings_write_coord_data(uint8_t coord_select, float *coord_data) memcpy_to_eeprom_with_checksum(addr,(char*)coord_data, sizeof(float)*N_AXIS); } + // Method to store Grbl global settings struct and version number into EEPROM void write_global_settings() { @@ -64,53 +59,51 @@ void write_global_settings() memcpy_to_eeprom_with_checksum(EEPROM_ADDR_GLOBAL, (char*)&settings, sizeof(settings_t)); } + // Method to reset Grbl global settings back to defaults. -void settings_reset(bool reset_all) { - // Reset all settings or only the migration settings to the new version. - if (reset_all) { - settings.steps_per_mm[X_AXIS] = DEFAULT_X_STEPS_PER_MM; - settings.steps_per_mm[Y_AXIS] = DEFAULT_Y_STEPS_PER_MM; - settings.steps_per_mm[Z_AXIS] = DEFAULT_Z_STEPS_PER_MM; - settings.pulse_microseconds = DEFAULT_STEP_PULSE_MICROSECONDS; - settings.default_feed_rate = DEFAULT_FEEDRATE; - settings.max_velocity[X_AXIS] = DEFAULT_RAPID_FEEDRATE; - settings.max_velocity[Y_AXIS] = DEFAULT_RAPID_FEEDRATE; - settings.max_velocity[Z_AXIS] = DEFAULT_RAPID_FEEDRATE; - settings.acceleration[X_AXIS] = DEFAULT_ACCELERATION; - settings.acceleration[Y_AXIS] = DEFAULT_ACCELERATION; - settings.acceleration[Z_AXIS] = DEFAULT_ACCELERATION; - settings.arc_tolerance = DEFAULT_ARC_TOLERANCE; - settings.invert_mask = DEFAULT_STEPPING_INVERT_MASK; - settings.junction_deviation = DEFAULT_JUNCTION_DEVIATION; - } - // New settings since last version +void settings_reset() { + settings.steps_per_mm[X_AXIS] = DEFAULT_X_STEPS_PER_MM; + settings.steps_per_mm[Y_AXIS] = DEFAULT_Y_STEPS_PER_MM; + settings.steps_per_mm[Z_AXIS] = DEFAULT_Z_STEPS_PER_MM; + settings.pulse_microseconds = DEFAULT_STEP_PULSE_MICROSECONDS; + settings.max_rate[X_AXIS] = DEFAULT_X_MAX_RATE; + settings.max_rate[Y_AXIS] = DEFAULT_Y_MAX_RATE; + settings.max_rate[Z_AXIS] = DEFAULT_Z_MAX_RATE; + settings.acceleration[X_AXIS] = DEFAULT_X_ACCELERATION; + settings.acceleration[Y_AXIS] = DEFAULT_Y_ACCELERATION; + settings.acceleration[Z_AXIS] = DEFAULT_Z_ACCELERATION; + settings.arc_tolerance = DEFAULT_ARC_TOLERANCE; + settings.step_invert_mask = DEFAULT_STEPPING_INVERT_MASK; + settings.dir_invert_mask = DEFAULT_DIRECTION_INVERT_MASK; + settings.junction_deviation = DEFAULT_JUNCTION_DEVIATION; settings.flags = 0; if (DEFAULT_REPORT_INCHES) { settings.flags |= BITFLAG_REPORT_INCHES; } if (DEFAULT_AUTO_START) { settings.flags |= BITFLAG_AUTO_START; } if (DEFAULT_INVERT_ST_ENABLE) { settings.flags |= BITFLAG_INVERT_ST_ENABLE; } + if (DEFAULT_INVERT_LIMIT_PINS) { settings.flags |= BITFLAG_INVERT_LIMIT_PINS; } if (DEFAULT_SOFT_LIMIT_ENABLE) { settings.flags |= BITFLAG_SOFT_LIMIT_ENABLE; } if (DEFAULT_HARD_LIMIT_ENABLE) { settings.flags |= BITFLAG_HARD_LIMIT_ENABLE; } if (DEFAULT_HOMING_ENABLE) { settings.flags |= BITFLAG_HOMING_ENABLE; } settings.homing_dir_mask = DEFAULT_HOMING_DIR_MASK; - settings.homing_feed_rate = DEFAULT_HOMING_FEEDRATE; - settings.homing_seek_rate = DEFAULT_HOMING_RAPID_FEEDRATE; + settings.homing_feed_rate = DEFAULT_HOMING_FEED_RATE; + settings.homing_seek_rate = DEFAULT_HOMING_SEEK_RATE; settings.homing_debounce_delay = DEFAULT_HOMING_DEBOUNCE_DELAY; settings.homing_pulloff = DEFAULT_HOMING_PULLOFF; settings.stepper_idle_lock_time = DEFAULT_STEPPER_IDLE_LOCK_TIME; - settings.decimal_places = DEFAULT_DECIMAL_PLACES; - settings.max_travel[X_AXIS] = DEFAULT_X_MAX_TRAVEL; - settings.max_travel[Y_AXIS] = DEFAULT_Y_MAX_TRAVEL; - settings.max_travel[Z_AXIS] = DEFAULT_Z_MAX_TRAVEL; + settings.max_travel[X_AXIS] = (-DEFAULT_X_MAX_TRAVEL); + settings.max_travel[Y_AXIS] = (-DEFAULT_Y_MAX_TRAVEL); + settings.max_travel[Z_AXIS] = (-DEFAULT_Z_MAX_TRAVEL); write_global_settings(); } + // Reads startup line from EEPROM. Updated pointed line string data. uint8_t settings_read_startup_line(uint8_t n, char *line) { uint16_t addr = n*(LINE_BUFFER_SIZE+1)+EEPROM_ADDR_STARTUP_BLOCK; if (!(memcpy_from_eeprom_with_checksum((char*)line, addr, LINE_BUFFER_SIZE))) { // Reset line with default value - line[0] = 0; + line[0] = 0; // Empty line settings_store_startup_line(n, line); return(false); } else { @@ -118,6 +111,21 @@ uint8_t settings_read_startup_line(uint8_t n, char *line) } } + +// Reads startup line from EEPROM. Updated pointed line string data. +uint8_t settings_read_build_info(char *line) +{ + if (!(memcpy_from_eeprom_with_checksum((char*)line, EEPROM_ADDR_BUILD_INFO, LINE_BUFFER_SIZE))) { + // Reset line with default value + line[0] = 0; // Empty line + settings_store_build_info(line); + return(false); + } else { + return(true); + } +} + + // Read selected coordinate data from EEPROM. Updates pointed coord_data value. uint8_t settings_read_coord_data(uint8_t coord_select, float *coord_data) { @@ -132,26 +140,18 @@ uint8_t settings_read_coord_data(uint8_t coord_select, float *coord_data) } } + // Reads Grbl global settings struct from EEPROM. uint8_t read_global_settings() { // Check version-byte of eeprom uint8_t version = eeprom_get_char(0); - if (version == SETTINGS_VERSION) { // Read settings-record and check checksum if (!(memcpy_from_eeprom_with_checksum((char*)&settings, EEPROM_ADDR_GLOBAL, sizeof(settings_t)))) { return(false); } } else { - if (version <= 4) { - // Migrate from settings version 4 to current version. - if (!(memcpy_from_eeprom_with_checksum((char*)&settings, 1, sizeof(settings_v4_t)))) { - return(false); - } - settings_reset(false); // Old settings ok. Write new settings only. - } else { - return(false); - } + return(false); } return(true); } @@ -159,28 +159,27 @@ uint8_t read_global_settings() { // A helper method to set settings from command line uint8_t settings_store_global_setting(int parameter, float value) { + if (value < 0.0) { return(STATUS_NEGATIVE_VALUE); } switch(parameter) { case 0: case 1: case 2: - if (value <= 0.0) { return(STATUS_SETTING_VALUE_NEG); } settings.steps_per_mm[parameter] = value; break; - case 3: settings.max_velocity[X_AXIS] = value; break; - case 4: settings.max_velocity[Y_AXIS] = value; break; - case 5: settings.max_velocity[Z_AXIS] = value; break; + case 3: settings.max_rate[X_AXIS] = value; break; + case 4: settings.max_rate[Y_AXIS] = value; break; + case 5: settings.max_rate[Z_AXIS] = value; break; case 6: settings.acceleration[X_AXIS] = value*60*60; break; // Convert to mm/min^2 for grbl internal use. case 7: settings.acceleration[Y_AXIS] = value*60*60; break; // Convert to mm/min^2 for grbl internal use. case 8: settings.acceleration[Z_AXIS] = value*60*60; break; // Convert to mm/min^2 for grbl internal use. - case 9: settings.max_travel[X_AXIS] = value; break; - case 10: settings.max_travel[Y_AXIS] = value; break; - case 11: settings.max_travel[Z_AXIS] = value; break; + case 9: settings.max_travel[X_AXIS] = -value; break; // Store as negative for grbl internal use. + case 10: settings.max_travel[Y_AXIS] = -value; break; // Store as negative for grbl internal use. + case 11: settings.max_travel[Z_AXIS] = -value; break; // Store as negative for grbl internal use. case 12: if (value < 3) { return(STATUS_SETTING_STEP_PULSE_MIN); } settings.pulse_microseconds = round(value); break; - case 13: settings.default_feed_rate = value; break; - case 14: settings.invert_mask = trunc(value); break; + case 13: settings.step_invert_mask = trunc(value); break; + case 14: settings.dir_invert_mask = trunc(value); break; case 15: settings.stepper_idle_lock_time = round(value); break; case 16: settings.junction_deviation = fabs(value); break; case 17: settings.arc_tolerance = value; break; - case 18: settings.decimal_places = round(value); break; case 19: if (value) { settings.flags |= BITFLAG_REPORT_INCHES; } else { settings.flags &= ~BITFLAG_REPORT_INCHES; } @@ -193,26 +192,33 @@ uint8_t settings_store_global_setting(int parameter, float value) { if (value) { settings.flags |= BITFLAG_INVERT_ST_ENABLE; } else { settings.flags &= ~BITFLAG_INVERT_ST_ENABLE; } break; - case 22: + case 22: // Reset to ensure change. Immediate re-init may cause problems. + if (value) { settings.flags |= BITFLAG_INVERT_LIMIT_PINS; } + else { settings.flags &= ~BITFLAG_INVERT_LIMIT_PINS; } + break; + case 23: if (value) { if (bit_isfalse(settings.flags, BITFLAG_HOMING_ENABLE)) { return(STATUS_SOFT_LIMIT_ERROR); } settings.flags |= BITFLAG_SOFT_LIMIT_ENABLE; } else { settings.flags &= ~BITFLAG_SOFT_LIMIT_ENABLE; } break; - case 23: + case 24: if (value) { settings.flags |= BITFLAG_HARD_LIMIT_ENABLE; } else { settings.flags &= ~BITFLAG_HARD_LIMIT_ENABLE; } limits_init(); // Re-init to immediately change. NOTE: Nice to have but could be problematic later. break; - case 24: + case 25: if (value) { settings.flags |= BITFLAG_HOMING_ENABLE; } - else { settings.flags &= ~BITFLAG_HOMING_ENABLE; } + else { + settings.flags &= ~BITFLAG_HOMING_ENABLE; + settings.flags &= ~BITFLAG_SOFT_LIMIT_ENABLE; // Force disable soft-limits. + } break; - case 25: settings.homing_dir_mask = trunc(value); break; - case 26: settings.homing_feed_rate = value; break; - case 27: settings.homing_seek_rate = value; break; - case 28: settings.homing_debounce_delay = round(value); break; - case 29: settings.homing_pulloff = value; break; + case 26: settings.homing_dir_mask = trunc(value); break; + case 27: settings.homing_feed_rate = value; break; + case 28: settings.homing_seek_rate = value; break; + case 29: settings.homing_debounce_delay = round(value); break; + case 30: settings.homing_pulloff = value; break; default: return(STATUS_INVALID_STATEMENT); } @@ -220,11 +226,12 @@ uint8_t settings_store_global_setting(int parameter, float value) { return(STATUS_OK); } + // Initialize the config subsystem void settings_init() { if(!read_global_settings()) { report_status_message(STATUS_SETTING_READ_FAIL); - settings_reset(true); + settings_reset(); report_grbl_settings(); } // Read all parameter data into a dummy variable. If error, reset to zero, otherwise do nothing. diff --git a/settings.h b/settings.h index 3369845..54a46a2 100644 --- a/settings.h +++ b/settings.h @@ -2,8 +2,8 @@ settings.h - eeprom configuration handling Part of Grbl + Copyright (c) 2011-2014 Sungeun K. Jeon Copyright (c) 2009-2011 Simen Svale Skogsrud - Copyright (c) 2011-2013 Sungeun K. Jeon Grbl is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -22,14 +22,15 @@ #ifndef settings_h #define settings_h -#include -#include "nuts_bolts.h" +#include "system.h" -#define GRBL_VERSION "0.9a" + +#define GRBL_VERSION "0.9f" +#define GRBL_VERSION_BUILD "20140706" // Version of the EEPROM data. Will be used to migrate existing data from older versions of Grbl // when firmware is upgraded. Always stored in byte 0 of eeprom -#define SETTINGS_VERSION 53 +#define SETTINGS_VERSION 8 // Define bit flag masks for the boolean settings in settings.flag. #define BITFLAG_REPORT_INCHES bit(0) @@ -38,6 +39,7 @@ #define BITFLAG_HARD_LIMIT_ENABLE bit(3) #define BITFLAG_HOMING_ENABLE bit(4) #define BITFLAG_SOFT_LIMIT_ENABLE bit(5) +#define BITFLAG_INVERT_LIMIT_PINS bit(6) // Define EEPROM memory address location values for Grbl settings and parameters // NOTE: The Atmega328p has 1KB EEPROM. The upper half is reserved for parameters and @@ -46,6 +48,7 @@ #define EEPROM_ADDR_GLOBAL 1 #define EEPROM_ADDR_PARAMETERS 512 #define EEPROM_ADDR_STARTUP_BLOCK 768 +#define EEPROM_ADDR_BUILD_INFO 992 // Define EEPROM address indexing for coordinate parameters #define N_COORDINATE_SYSTEM 6 // Number of supported work coordinate systems (from index 1) @@ -58,24 +61,21 @@ // Global persistent settings (Stored from byte EEPROM_ADDR_GLOBAL onwards) typedef struct { float steps_per_mm[N_AXIS]; - uint8_t microsteps; - uint8_t pulse_microseconds; - float default_feed_rate; - float default_seek_rate; - uint8_t invert_mask; - float arc_tolerance; + float max_rate[N_AXIS]; float acceleration[N_AXIS]; + float max_travel[N_AXIS]; + uint8_t pulse_microseconds; + uint8_t step_invert_mask; + uint8_t dir_invert_mask; + uint8_t stepper_idle_lock_time; // If max value 255, steppers do not disable. float junction_deviation; + float arc_tolerance; uint8_t flags; // Contains default boolean settings uint8_t homing_dir_mask; float homing_feed_rate; float homing_seek_rate; uint16_t homing_debounce_delay; float homing_pulloff; - uint8_t stepper_idle_lock_time; // If max value 255, steppers do not disable. - uint8_t decimal_places; - float max_velocity[N_AXIS]; - float max_travel[N_AXIS]; // uint8_t status_report_mask; // Mask to indicate desired report data. } settings_t; extern settings_t settings; @@ -92,6 +92,10 @@ void settings_store_startup_line(uint8_t n, char *line); // Reads an EEPROM startup line to the protocol line variable uint8_t settings_read_startup_line(uint8_t n, char *line); +void settings_store_build_info(char *line); + +uint8_t settings_read_build_info(char *line); + // Writes selected coordinate data to EEPROM void settings_write_coord_data(uint8_t coord_select, float *coord_data); diff --git a/sim/.gitignore b/sim/.gitignore index 59a9a43..91eb721 100644 --- a/sim/.gitignore +++ b/sim/.gitignore @@ -1 +1,2 @@ grbl_sim.exe +*.dat diff --git a/sim/Makefile b/sim/Makefile index bece555..176587b 100644 --- a/sim/Makefile +++ b/sim/Makefile @@ -15,11 +15,14 @@ # You should have received a copy of the GNU General Public License # along with Grbl. If not, see . -OBJECTS = main.o simulator.o runtime.o ../protocol.o ../planner.o ../settings.o ../print.o ../nuts_bolts.o eeprom.o serial.o avr/pgmspace.o avr/interrupt.o util/delay.o util/floatunsisf.o ../stepper.o ../gcode.o ../spindle_control.o ../motion_control.o ../limits.o ../report.o ../coolant_control.o +PLATFORM = WINDOWS + +OBJECTS = main.o simulator.o serial.o ../main.o ../protocol.o ../planner.o ../settings.o ../print.o ../nuts_bolts.o eeprom.o ../serial.o avr/pgmspace.o avr/interrupt.o avr/io.o util/delay.o util/floatunsisf.o ../stepper.o ../gcode.o ../spindle_control.o ../motion_control.o ../limits.o ../report.o ../coolant_control.o ../probe.o ../system.o platform_$(PLATFORM).o CLOCK = 16000000 EXE_NAME = grbl_sim.exe -COMPILE = $(CC) -Wall -Os -DF_CPU=$(CLOCK) -include config.h -I. - +COMPILE = $(CC) -Wall -g -DF_CPU=$(CLOCK) -include config.h -I. -DPLAT_$(PLATFORM) +LINUX_LIBRARIES = -lrt -pthread +WINDOWS_LIBRARIES = # symbolic targets: all: main @@ -30,15 +33,18 @@ clean: # file targets: main: $(OBJECTS) - $(COMPILE) -o $(EXE_NAME) $(OBJECTS) -lm + $(COMPILE) -o $(EXE_NAME) $(OBJECTS) -lm $($(PLATFORM)_LIBRARIES) %.o: %.c - $(COMPILE) -c $< -o $@ - -../protocol.o: ../protocol.c - $(COMPILE) -include rename_execute_runtime.h -c $< -o $@ + $(COMPILE) -c $< -o $@ ../planner.o: ../planner.c $(COMPILE) -include planner_inject_accessors.c -c $< -o $@ +../serial.o: ../serial.c + $(COMPILE) -include serial_hooks.h -c $< -o $@ + +../main.o: ../main.c + $(COMPILE) -include rename_main.h -c $< -o $@ + diff --git a/sim/README.md b/sim/README.md index 6e266b4..19fd378 100644 --- a/sim/README.md +++ b/sim/README.md @@ -9,3 +9,10 @@ What can you do with Grbl Sim? - Visualize a g-code program by having the simulator parse and execute to a GUI. Fluctuations in feed rates by the acceleration planner can be viewed as well. - A powerful debugging tool for development. - Each of the AVR functions are replaced with dummy functions, like the stepper ISR. These could be written to whatever you need. For example, output simulated step pulses over time and examine its performance. + +Realtime modifications by Adam Shelly: + + Simulates Atmel hardware in separate thread. Runs in aproximate realtime. + + On Linux, use `socat PTY,raw,link=/dev/ttyFAKE,echo=0 "EXEC:'./grbl_sim.exe -n -s step.out -b block.out',pty,raw,echo=0"` to create a fake serial port connected to the simulator. This is useful for testing grbl interface software. + diff --git a/sim/avr/interrupt.c b/sim/avr/interrupt.c index f47e11d..2bcbe0d 100644 --- a/sim/avr/interrupt.c +++ b/sim/avr/interrupt.c @@ -21,22 +21,115 @@ */ #include "interrupt.h" +#include "io.h" + +//pseudo-Interrupt vector table +isr_fp compa_vect[6]={0}; +isr_fp compb_vect[6]={0}; +isr_fp ovf_vect[6]={0}; + + +void sei() {io.sreg|=SEI;} +void cli() {io.sreg&=~SEI;} + + + +int16_t sim_scaling[8]={0,1,8,64,256,1024,1,1}; //clock scalars +//Timer/Counter modes: these are incomplete, but enough for this application +enum sim_wgm_mode { + wgm_NORMAL, + wgm_CTC, + wgm_FAST_PWM, + wgm_PHASE_PWM, + wgm_PH_F_PWM, + wgm_RESERVED +}; + +enum sim_wgm_mode sim_wgm0[4] = {wgm_NORMAL,wgm_PHASE_PWM,wgm_CTC,wgm_FAST_PWM}; +enum sim_wgm_mode sim_wgmN[8] = {wgm_NORMAL,wgm_PHASE_PWM,wgm_PHASE_PWM,wgm_PH_F_PWM, + wgm_CTC, wgm_FAST_PWM, wgm_FAST_PWM, wgm_FAST_PWM}; + +void timer_interrupts() { + int i; + uint8_t ien = io.sreg&SEI; //interrupts enabled? + io.prescaler++; + + //all clocks + for (i=0;i<2;i++){ + + uint8_t cs = io.tccrb[i]&7; //clock select bits + int16_t increment = sim_scaling[cs]; + //check scaling to see if timer fires + if (increment && (io.prescaler&(increment-1))==0) { + + //select waveform generation mode + enum sim_wgm_mode mode; + if (i==0 || i==2) { //(T0 and T2 are different from rest) + uint8_t wgm = io.tccra[i]&3; //look at low 2 bits + mode = sim_wgm0[wgm]; + } + else { + uint8_t wgm = ((io.tccrb[i]&8)>>1) | (io.tccra[i]&3); //only using 3 bits for now + mode = sim_wgmN[wgm]; + } + + //tick + io.tcnt[i]++; + //comparators + if ((io.timsk[i]&(1< - -// dummy register variables -extern uint16_t timsk0; -extern uint16_t timsk1; -extern uint16_t timsk2; -extern uint16_t tcnt0; -extern uint16_t tcnt2; -extern uint16_t tccr0b; -extern uint16_t tccr0a; -extern uint16_t tccr2a; -extern uint16_t tccr2b; -extern uint16_t tccr1b; -extern uint16_t tccr1a; -extern uint16_t ocr1a; -extern uint16_t ocr2a; -extern uint16_t pcmsk0; -extern uint16_t pcicr; - // macros to turn avr interrupts into regular functions -#define TIMER1_COMPA_vect +//#define TIMER1_COMPA_vect #define ISR(a) void interrupt_ ## a () -// enable interrupts does nothing in the simulation environment -void sei(); +// Stub of the timer interrupt functions we need +void interrupt_TIMER0_COMPA_vect(); +void interrupt_TIMER1_COMPA_vect(); +void interrupt_TIMER0_OVF_vect(); +void interrupt_SERIAL_UDRE(); +void interrupt_SERIAL_RX(); + + +//pseudo-Interrupt vector table +typedef void(*isr_fp)(void); +extern isr_fp compa_vect[6]; +extern isr_fp compb_vect[6]; +extern isr_fp ovf_vect[6]; + + +// enable interrupts now does something in the simulation environment +#define SEI 0x80 +void sei(); +void cli(); + +//simulate timer operation +void timer_interrupts(); + -// dummy macros for interrupt related registers -#define TIMSK0 timsk0 -#define TIMSK1 timsk1 -#define TIMSK2 timsk2 -#define OCR1A ocr1a -#define OCR2A ocr2a -#define OCIE1A 0 -#define OCIE2A 0 -#define TCNT0 tcnt0 -#define TCNT2 tcnt2 -#define TCCR0B tccr0b -#define TCCR0A tccr0a -#define TCCR1A tccr1a -#define TCCR1B tccr1b -#define TCCR2A tccr2a -#define TCCR2B tccr2b -#define CS21 0 -#define CS10 0 -#define WGM13 0 -#define WGM12 0 -#define WGM11 0 -#define WGM10 0 -#define WGM21 0 -#define COM1A0 0 -#define COM1B0 0 -#define TOIE0 0 -#define TOIE2 0 -#define PCICR pcicr #endif diff --git a/sim/avr/io.c b/sim/avr/io.c new file mode 100644 index 0000000..8290fec --- /dev/null +++ b/sim/avr/io.c @@ -0,0 +1,4 @@ +#include "io.h" + +// dummy register variables +volatile io_sim_t io={{0}}; diff --git a/sim/avr/io.h b/sim/avr/io.h index 885021a..df8a515 100644 --- a/sim/avr/io.h +++ b/sim/avr/io.h @@ -1,5 +1,6 @@ /* - io.h - dummy replacement for the avr include of the same name + interrupt.h - replacement for the avr include of the same name to provide + dummy register variables and macros Part of Grbl Simulator @@ -19,3 +20,182 @@ along with Grbl. If not, see . */ + +#ifndef io_h +#define io_h + +#include + +union hilo16 { + uint16_t w; + struct { + uint8_t l; //TODO: check that these are right order on x86. Doesn't matter for current usage, but might someday + uint8_t h; + }; +}; + +enum { + SIM_A, SIM_B, SIM_C, SIM_D, SIM_E, + SIM_F, SIM_G, SIM_H, SIM_J, SIM_K, SIM_L, + SIM_PORT_COUNT +}; + +#define SIM_N_TIMERS 6 + + +// dummy register variables +typedef struct io_sim { + uint8_t ddr[SIM_PORT_COUNT]; + uint8_t port[SIM_PORT_COUNT]; + uint8_t pin[SIM_PORT_COUNT]; + uint8_t timsk[SIM_N_TIMERS]; + uint16_t ocra[SIM_N_TIMERS]; + uint16_t ocrb[SIM_N_TIMERS]; + uint16_t ocrc[SIM_N_TIMERS]; + uint16_t tcnt[SIM_N_TIMERS]; //tcint0 is really only 8bit + uint8_t tccra[SIM_N_TIMERS]; + uint8_t tccrb[SIM_N_TIMERS]; + uint8_t tifr[SIM_N_TIMERS]; + uint8_t pcicr; + uint8_t pcmsk[3]; + uint8_t ucsr0[3]; + uint8_t udr[3]; + union hilo16 ubrr0; + + uint16_t prescaler; //continuously running + uint8_t sreg; + + +} io_sim_t; +volatile extern io_sim_t io; + + + + +// dummy macros for interrupt related registers +#define PORTA io.port[SIM_A] +#define PORTB io.port[SIM_B] +#define PORTC io.port[SIM_C] +#define PORTD io.port[SIM_D] +#define PORTE io.port[SIM_E] +#define PORTF io.port[SIM_F] +#define PORTG io.port[SIM_G] +#define PORTH io.port[SIM_H] +#define PORTJ io.port[SIM_J] +#define PORTK io.port[SIM_K] +#define PORTL io.port[SIM_L] + +#define DDRA io.ddr[SIM_A] +#define DDRB io.ddr[SIM_B] +#define DDRC io.ddr[SIM_C] +#define DDRD io.ddr[SIM_D] +#define DDRE io.ddr[SIM_E] +#define DDRF io.ddr[SIM_F] +#define DDRG io.ddr[SIM_G] +#define DDRH io.ddr[SIM_H] +#define DDRJ io.ddr[SIM_J] + +#define PINA io.pin[SIM_A] +#define PINB io.pin[SIM_B] +#define PINC io.pin[SIM_C] +#define PIND io.pin[SIM_D] +#define PINE io.pin[SIM_E] +#define PINF io.pin[SIM_F] +#define PING io.pin[SIM_G] +#define PINH io.pin[SIM_H] +#define PINJ io.pin[SIM_J] +#define PINK io.pin[SIM_K] +#define PINL io.pin[SIM_L] + + +#define TIMSK0 io.timsk[0] +#define TIMSK1 io.timsk[1] +#define TIMSK2 io.timsk[2] +#define TIMSK3 io.timsk[3] +#define TIMSK4 io.timsk[4] +#define TIMSK5 io.timsk[5] + + +#define SIM_TOV 0 +#define SIM_OCA 1 +#define SIM_OCB 2 +#define SIM_OCC 3 +#define SIM_ICI 5 + +#define OCIE0A SIM_OCA +#define OCIE0B SIM_OCB +#define TOIE0 SIM_TOV + +#define ICIE1 SIM_ICI +#define OCIE1C SIM_OCC +#define OCIE1B SIM_OCB +#define OCIE1A SIM_OCA +#define TOIE1 SIM_ICI + +#define ICIE2 SIM_ICI +#define OCIE2C SIM_OCC +#define OCIE2B SIM_OCB +#define OCIE2A SIM_OCA +#define TOIE2 SIM_TOV + +#define OCR0A io.ocra[0] +#define OCR1A io.ocra[1] +#define OCR2A io.ocra[2] + //There are more.. + + +#define TCNT0 io.tcnt[0] +#define TCNT1 io.tcnt[1] +#define TCNT2 io.tcnt[2] + +#define TCCR0B io.tccra[0] +#define TCCR0A io.tccrb[0] +#define TCCR1A io.tccra[1] +#define TCCR1B io.tccrb[1] +#define TCCR2A io.tccra[2] +#define TCCR2B io.tccrb[2] + +#define CS00 0 +#define CS01 1 +#define CS12 2 +#define CS11 1 +#define CS10 0 +#define CS21 1 + +#define WGM13 4 +#define WGM12 3 +#define WGM11 1 +#define WGM10 0 +#define WGM21 1 + +#define COM1A1 7 +#define COM1A0 6 +#define COM1B1 5 +#define COM1B0 4 +#define COM1C1 3 +#define COM1C0 2 + + +#define PCICR io.pcicr +#define PCIE0 0 +#define PCIE1 1 + +//serial channel +#define UCSR0A io.ucsr0[SIM_A] +#define UCSR0B io.ucsr0[SIM_B] +#define UDR0 io.udr[0] +#define UDRIE0 0 +#define RXCIE0 1 +#define RXEN0 2 +#define TXEN0 3 +#define U2X0 4 +#define UBRR0H io.ubrr0.h +#define UBRR0L io.ubrr0.l + +#define PCMSK0 io.pcmsk[0] +#define PCMSK1 io.pcmsk[1] +#define PCMSK2 io.pcmsk[2] + + + +#endif diff --git a/sim/avr/wdt.h b/sim/avr/wdt.h new file mode 100644 index 0000000..482f4fd --- /dev/null +++ b/sim/avr/wdt.h @@ -0,0 +1 @@ +uint16_t wdt; diff --git a/sim/config.h b/sim/config.h index 404accd..5baa20c 100644 --- a/sim/config.h +++ b/sim/config.h @@ -24,94 +24,5 @@ #include "../config.h" #include - -// dummy register variables implemented in simulator.c -extern uint8_t stepping_ddr; -extern uint8_t stepping_port; -extern uint8_t spindle_ddr; -extern uint8_t spindle_port; -extern uint8_t limit_ddr; -extern uint8_t limit_port; -extern uint8_t limit_int_reg; -extern uint8_t pinout_ddr; -extern uint8_t pinout_port; -extern uint8_t pinout_int_reg; -extern uint8_t coolant_flood_ddr; -extern uint8_t coolant_flood_port; - -// ReDefine pin-assignments -#undef STEPPING_DDR -#define STEPPING_DDR stepping_ddr -#undef STEPPING_PORT -#define STEPPING_PORT stepping_port -#undef X_STEP_BIT -#define X_STEP_BIT 2 // Uno Digital Pin 2 -#undef Y_STEP_BIT -#define Y_STEP_BIT 3 // Uno Digital Pin 3 -#undef Z_STEP_BIT -#define Z_STEP_BIT 4 // Uno Digital Pin 4 -#undef X_DIRECTION_BIT -#define X_DIRECTION_BIT 5 // Uno Digital Pin 5 -#undef Y_DIRECTION_BIT -#define Y_DIRECTION_BIT 6 // Uno Digital Pin 6 -#undef Z_DIRECTION_BIT -#define Z_DIRECTION_BIT 7 // Uno Digital Pin 7 - -#undef STEPPERS_DISABLE_DDR -#define STEPPERS_DISABLE_DDR stepping_ddr -#undef STEPPERS_DISABLE_PORT -#define STEPPERS_DISABLE_PORT stepping_port -#undef STEPPERS_DISABLE_BIT -#define STEPPERS_DISABLE_BIT 0 // Uno Digital Pin 8 - -#undef LIMIT_DDR -#define LIMIT_DDR limit_ddr -#undef LIMIT_PORT -#define LIMIT_PORT limit_port -#undef LIMIT_PIN -#define LIMIT_PIN limit_port -#undef X_LIMIT_BIT -#define X_LIMIT_BIT 1 // Uno Digital Pin 9 -#undef Y_LIMIT_BIT -#define Y_LIMIT_BIT 2 // Uno Digital Pin 10 -#undef Z_LIMIT_BIT -#define Z_LIMIT_BIT 3 // Uno Digital Pin 11 -#undef LIMIT_INT -#define LIMIT_INT 0 -#undef LIMIT_PCMSK -#define LIMIT_PCMSK limit_int_reg - -#undef SPINDLE_ENABLE_DDR -#define SPINDLE_ENABLE_DDR spindle_ddr -#undef SPINDLE_ENABLE_PORT -#define SPINDLE_ENABLE_PORT spindle_port -#undef SPINDLE_ENABLE_BIT -#define SPINDLE_ENABLE_BIT 4 // Uno Digital Pin 12 - -#undef SPINDLE_DIRECTION_DDR -#define SPINDLE_DIRECTION_DDR spindle_ddr -#undef SPINDLE_DIRECTION_PORT -#define SPINDLE_DIRECTION_PORT spindle_port -#undef SPINDLE_DIRECTION_BIT -#define SPINDLE_DIRECTION_BIT 5 // Uno Digital Pin 13 - -#undef PINOUT_DDR -#define PINOUT_DDR pinout_ddr -#undef PINOUT_PORT -#define PINOUT_PORT pinout_port -#undef PINOUT_PIN -#define PINOUT_PIN pinout_port -#undef PINOUT_PCMSK -#define PINOUT_PCMSK pinout_int_reg -#undef PINOUT_INT -#define PINOUT_INT 0 - -#undef COOLANT_FLOOD_DDR -#define COOLANT_FLOOD_DDR coolant_flood_ddr -#undef COOLANT_FLOOD_PORT -#define COOLANT_FLOOD_PORT coolant_flood_port -#undef COOLANT_FLOOD_BIT -#define COOLANT_FLOOD_BIT 0 - - +#define AUTO_REPORT_MOVE_DONE #endif diff --git a/sim/eeprom.c b/sim/eeprom.c index 5c3c3f3..0291e85 100644 --- a/sim/eeprom.c +++ b/sim/eeprom.c @@ -21,18 +21,75 @@ */ // These are never called in the simulator +#include +#define MAX_EEPROM_SIZE 4096 //4KB EEPROM in Mega + + +FILE* eeprom_create_empty_file(){ + FILE* fp = fopen("EEPROM.DAT","w+b"); + int i; + if (fp){ + for(i=0;i 0; size--) { + checksum = (checksum << 1) || (checksum >> 7); + checksum += *source; + eeprom_put_char(destination++, *(source++)); + } + eeprom_put_char(destination, checksum); } int memcpy_from_eeprom_with_checksum(char *destination, unsigned int source, unsigned int size) { - return 0; + unsigned char data, checksum = 0; + for(; size > 0; size--) { + data = eeprom_get_char(source++); + checksum = (checksum << 1) || (checksum >> 7); + checksum += data; + *(destination++) = data; + } + return(checksum == eeprom_get_char(source)); } // end of file diff --git a/sim/eeprom.h b/sim/eeprom.h new file mode 100644 index 0000000..6b9a562 --- /dev/null +++ b/sim/eeprom.h @@ -0,0 +1,3 @@ + +#include "../eeprom.h" +void eeprom_close(); diff --git a/sim/main.c b/sim/main.c index d157ac3..e2a4e2e 100644 --- a/sim/main.c +++ b/sim/main.c @@ -34,18 +34,106 @@ #include "simulator.h" +arg_vars_t args; +const char* progname; + +int usage(const char* badarg){ + if (badarg){ + printf("Unrecognized option %s\n",badarg); + } + printf("Usage: \n" + "%s [options] [time_step] [block_file]\n" + " Options:\n" + " -r : minimum time step for printing stepper values. Default=0=no print.\n" + " -t