k3ng_rotator_controller

/* Arduino Rotator Controller

   Anthony Good
   K3NG
   anthony.good@gmail.com
  

  
   Code contributions, testing, ideas, bug fixes, hardware, support, encouragement, and/or bourbon provided by:
     John W3SA
     Gord VO1GPK
     Anthony M0UPU
     Pete VE5VA
     Marcin SP5IOU
     Hjalmar OZ1JHM
     Sverre LA3ZA
     Bent OZ1CT
     Erick WB6KCN
     Norm N3YKF
     Jan OK2ZAW
     Jim M0CKE
     Mike AD0CZ
     Paolo IT9IPQ
     Antonio IZ7DDA
     Johan PA3FPQ
     Jurgen PE1LWT
     Gianfranco IZ8EWD 
     Jasper PA2J
     ...and others
  
   Translations provided by
     Maximo EA1DDO
     Jan OK2ZAW
     Paolo IT9IPQ
     Ismael PY4PI


   (If you contributed something and I forgot to put your name and call in here, *please* email me!)
  
 ***************************************************************************************************************
 *
 *  This program is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License
 *
 *                            http://creativecommons.org/licenses/by-nc-sa/3.0/
 *
 *                        http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode
 *
 *
 ***************************************************************************************************************
  
  
  
  
                            All copyrights are the property of their respective owners
  
 
 
 Full documentation is currently located here: https://docs.google.com/document/d/1Q4_X2XdndylohjhZWsaAUNl1UIqYGt9Kxp1VM70XJGM/edit?usp=sharing

      Rules for using this code:

          Rule #1: Read the documentation.
  
          Rule #2: Refer to rule #1.

          Rule #3: Help others.

          Rule #4: Have fun.

  
  New in this release:
  
   HH-12 encoder support 
   OPTION_PULSE_IGNORE_AMBIGUOUS_PULSES
   \P PWM command is now \W
   \P is Park command
   park_in_progress_pin  // goes high when a park has been initiated and rotation is in progress 
   parked_pin            // goes high when in a parked position 
    heading_reading_inhibit_pin 
    FEATURE_LIMIT_SENSE 
   #define az_limit_sense_pin 0  // input - low stops azimuthal rotation 
   #define el_limit_sense_pin 0  // input - low stops elevation rotation 
    \Axxx command - manually set azimuth, xxx = azimuth (FEATURE_AZ_POSITION_ROTARY_ENCODER or FEATURE_AZ_POSITION_PULSE_INPUT only) 
    \Bxxx command - manually set elevation, xxx = elevation (FEATURE_EL_POSITION_POTENTIOMETER or FEATURE_EL_POSITION_ROTARY_ENCODER only) 
    fixed bug with preset encoder start and kill button
    FEATURE_AZ_POSITION_INCREMENTAL_ENCODER
    FEATURE_EL_POSITION_INCREMENTAL_ENCODER
    OPTION_INCREMENTAL_ENCODER_PULLUPS
   #define AZ_POSITION_INCREMENTAL_ENCODER_PULSES_PER_REV 8000.0
   #define EL_POSITION_INCREMENTAL_ENCODER_PULSES_PER_REV 8000.0
    AZIMUTH_STARTING_POINT_DEFAULT and AZIMUTH_ROTATION_CAPABILITY_DEFAULT are now persistent
    Yaesu P35, P45, and Z commands no longer write to eeprom 
    control_port points to the hardware port for computer interface
    remote_unit_port points to the hardware port for interfacing to remote unit
    removed OPTION_SERIAL1, 2, 3, 4
    SS command: SS0 = control port, SS1 = remote unit port
    No longer need to manually uncomment hh12.h or object declarations
    No longer need to manually uncomment LiquidCrystal lcd() 
    No longer need to manually uncomment Adafruit_ADXL345 accel 
    No longer need to manually uncomment ADXL345 accel 
    No longer need to manually uncomment Adafruit_LSM303 lsm;
    No longer need to manually uncomment HMC5883L compass;
    FEATURE_4_BIT_LCD_DISPLAY 
    FEATURE_ADAFRUIT_I2C_LCD 
    FEATURE_YOURDUINO_I2C_LCD 
    FEATURE_RFROBOT_I2C_DISPLAY 
    No longer need to uncomment:
     FEATURE_LCD_DISPLAY 
     FEATURE_I2C_LCD 
     any include files 
    serial led duration now set by SERIAL_LED_TIME_MS
   #define CONTROL_PORT_MAPPED_TO &Serial  // change this line to map the control port to a different serial port (Serial1, Serial2, etc.) 
   #define REMOTE_PORT_MAPPED_TO &Serial1  // change this line to map the remote_unit port to a different serial port 
    start of remote unit pin control
    remote unit pin control (add 100 to a pin number define to map to remote unit pin) 
    FEATURE_CLOCK 
    FEATURE_MOON_TRACKING 
   #define DEFAULT_LATITUDE 40.889958 
   #define DEFAULT_LONGITUDE -75.585972 
  
   #define INTERNAL_CLOCK_CORRECTION 0.00145
  
    \C - show clock 
    \O - set clock \OYYYYMMDDHHmm 
    \Mx - x = 0: deactive moon tracking; x = 1: activate moon tracking 
    \Gxxxxxx - set coordinates via grid square (example: \GFN20EV) 
  
    Park is now deactivated when issuing a Yaesu command (other than C) or when doing manual rotation
    FEATURE_GPS
   #define GPS_PORT_MAPPED_TO &Serial2 
   #define GPS_PORT_BAUD_RATE 9600 
   #define SYNC_TIME_WITH_GPS 1 
   #define SYNC_COORDINATES_WITH_GPS 1 
   #define GPS_SYNC_PERIOD_SECONDS 10  // how long to consider internal clock syncronized after a GPS reading 
   #define GPS_VALID_FIX_AGE_MS 10000  // consider a GPS reading valid if the fix age is less than this 
    FEATURE_SUN_TRACKING 
      \Ux - x = 0: deactive sun tracking; x = 1: activate sun tracking  
    FEATURE_AZ_POSITION_INCREMENTAL_ENCODER & FEATURE_EL_POSITION_INCREMENTAL_ENCODER coding
    Updated debug output format
    \XS - calibration az and el to sun heading - there are calculation bugs
    fixed initialize_serial() compilaton error when neither yaesu or easycom protocol is selected in features
    fixed bugs in \XS and \XM
    \XS and\XM now working on all sensor types 
    moon_tracking_active_pin   
    sun_tracking_active_pin      
    moon_tracking_activate_line    
    sun_tracking_activate_line     
    moon_tracking_button  
    sun_tracking_button   
    \A azimuth calibration now also works with sensors other than rotary encoders and pulse input
    \B elevation calibration now also works with sensors other than rotary encoders and pulse input
    OPTION_BUTTON_RELEASE_NO_SLOWDOWN 
    Fixed God-awful bug that caused Arduino to crash when running FEATURE_GPS.  Note to self: never declare a char in a switch case.  It causes unpredicatable, unexplainable evil stuff to occur.
    Fixed bug with elevation PWM
     gps_sync pin - goes high when clock is GPS synchronized 
     FEATURE_RTC_PCF8583 
    \O command also programs realtime clocks now
    Fixed bug in PWM outputs when changing direction
    Ethernet now working with backslash commands, Yaesu commands, and Easycom commands
    Fixed bug in Easycom (non-standard) AZ and EL commands
    Ethernet remote unit slave support (slave only, master using ethernet not done yet)
    #define GPS_MIRROR_PORT &Serial3   
    OPTION_DISPLAY_SMALL_CLOCK
      #define LCD_SMALL_CLOCK_POSITION LEFT
    OPTION_DISPLAY_GPS_INDICATOR
      #define LCD_GPS_INDICATOR_POSITION RIGHT
    OPTION_DISPLAY_MOON_TRACKING_CONTINUOUSLY
      #define LCD_MOON_TRACKING_ROW 3
      #define LCD_MOON_TRACKING_UPDATE_INTERVAL 5000    
    OPTION_DISPLAY_SUN_TRACKING_CONTINUOUSLY
      #define LCD_SUN_TRACKING_ROW 4
      #define LCD_SUN_TRACKING_UPDATE_INTERVAL 5000  
    #define LCD_ROWS 4  
    fixed bug with Yourduino LCD display initialization (thanks PA3FPQ)
    added GPS counters to debug output
    FEATURE_POWER_SWITCH
      #define POWER_SWITCH_IDLE_TIMEOUT 15  (unit: minutes)
      #define power_switch 0
    OPTION_DISPLAY_MOON_OR_SUN_TRACKING_CONDITIONAL
      #define LCD_MOON_OR_SUN_TRACKING_CONDITIONAL_ROW 3
    OPTION_DISPLAY_BIG_CLOCK
    #define GPS_UPDATE_LATENCY_COMPENSATION_MS 200
    #define AZIMUTH_CALIBRATION_FROM_ARRAY {180,630} 
    #define AZIMUTH_CALIBRATION_TO_ARRAY {180,630}
    #define ELEVATION_CALIBRATION_FROM_ARRAY {-180,0,180}
    #define ELEVATION_CALIBRATION_TO_ARRAY {-180,0,180}
    #define rotate_cw_ccw  0
    #define az_stepper_motor_direction 0
    #define el_stepper_motor_direction 0
    bug fix for long clock display
    performance improvement for az / el display on LCD
    #define az_stepper_motor_pulse 0
    #define el_stepper_motor_pulse 0  
    OPTION_DISPLAY_ALT_HHMM_CLOCK_AND_MAIDENHEAD 
    #define LCD_SMALL_CLOCK_AND_MAIDENHEAD_POSITION 
    #define LCD_SMALL_CLOCK_AND_MAIDENHEAD_ROW 4 
    #define LCD_GPS_INDICATOR_ROW 1 
    OPTION_EXTERNAL_ANALOG_REFERENCE
    more debug_print conversion

    OPTION_DISPLAY_SMALL_CLOCK renamed to OPTION_DISPLAY_HHMM_CLOCK 
    LCD_SMALL_CLOCK_POSITION renamed to LCD_HHMM_CLOCK_POSITION 
    OPTION_DISPLAY_HHMMSS_CLOCK 
    OPTION_DISPLAY_DISABLE_DIRECTION_STATUS 
    OPTION_DISPLAY_SMALL_CLOCK_AND_MAIDENHEAD renamed to OPTION_DISPLAY_ALT_HHMM_CLOCK_AND_MAIDENHEAD 
    OPTION_DISPLAY_CONSTANT_HHMMSS_CLOCK_AND_MAIDENHEAD 

    #define LCD_HHMM_CLOCK_POSITION LEFT          //LEFT or RIGHT  
    #define LCD_HHMMSS_CLOCK_POSITION LEFT          //LEFT or RIGHT 
    #define LCD_ALT_HHMM_CLOCK_AND_MAIDENHEAD_POSITION LEFT 
    #define LCD_ALT_HHMM_CLOCK_AND_MAIDENHEAD_ROW 1 
    #define LCD_CONSTANT_HHMMSS_CLOCK_AND_MAIDENHEAD_POSITION LEFT 
    #define LCD_CONSTANT_HHMMSS_CLOCK_AND_MAIDENHEAD_ROW 1 

    OPTION_DISPLAY_DISABLE_DIRECTION_STATUS changed to OPTION_DISPLAY_DIRECTION_STATUS 
    worked on FEATURE_TWO_DECIMAL_PLACE_HEADINGS
    LANGUAGE_ENGLISH

    Easycom improvements - space and LF are now also command delimiters.  Also fixed bug with one decimal place.  Works with PSTRotator
    Fixed issue with LCD display updating when target az or target el was changed during rotation
    I2C_LCD_COLOR also applies to Yourduino LCD display

    HH-12 elevation bug fix

    FEATURE_MASTER_WITH_SERIAL_SLAVE
    FEATURE_MASTER_WITH_ETHERNET_SLAVE
    FEATURE_EL_POSITION_MEMSIC_2125  under development - not working yet

    fixed a bug with azimuth and elevation offset zeroing out first decimal place
    Ethernet master/slave link!
    #define ETHERNET_SLAVE_IP_ADDRESS 192,168,1,173
    #define ETHERNET_SLAVE_TCP_PORT 23
    #define ETHERNET_SLAVE_RECONNECT_TIME_MS 250

    Changed master/slave AZ and EL command result format: AZxxx.xxxxxx EL+xxx.xxxxxx
    Slave CL command

    OPTION_SYNC_MASTER_CLOCK_TO_SLAVE

    fixed "if (clock_status == SLAVE_SYNC) {clock_status = FREE_RUNNING;}" compile error

    OPTION_CLOCK_ALWAYS_HAVE_HOUR_LEADING_ZERO

    OPTION_DISABLE_HMC5883L_ERROR_CHECKING
    HARDWARE_EA4TX_ARS_USB
    HARDWARE_M0UPU
    rotator_hardware.h file
    rotator_features_ea4tx_ars_usb.h
    rotator_pins_ea4tx_ars_usb.h
    #define AZ_POSITION_INCREMENTAL_ENCODER_PULSES_PER_REV 2000.0
    #define EL_POSITION_INCREMENTAL_ENCODER_PULSES_PER_REV 2000.0

    FEATURE_EL_POSITION_INCREMENTAL_ENCODER now does 360 degrees of rotation rather than 720

    OPTION_PRESET_ENCODER_0_360_DEGREES

    service_rotation() - fixed rollover bug with az and el slow down

    Fixed decimal place issue with Easycom AZ and EL query commands
    FEATURE_EL_POSITION_INCREMENTAL_ENCODER - fixed storage and retrieval of elevation in eeprom
    Bug fix - stop command wouldn't work when in slow down
    AZ_INITIALLY_IN_SLOW_DOWN_PWM
    EL_INITIALLY_IN_SLOW_DOWN_PWM 

    Fixed compilation error when FEATURE_JOYSTICK_CONTROL is activated and FEATURE_ELEVATION_CONTROL is disabled

  Not documented yet: ---------------------------------------------------------------------------------------------

    FEATURE_ANALOG_OUTPUT_PINS (rotator_features.h)
    FEATURE_AZ_POSITION_LSM303 is now FEATURE_AZ_POSITION_ADAFRUIT_LSM303 (rotator_features.h)
    FEATURE_EL_POSITION_LSM303 is now FEATURE_EL_POSITION_ADAFRUIT_LSM303 (rotator_features.h)
    LANGUAGE_CZECH (rotator_features.h)

    FEATURE_AZ_POSITION_POLOLU_LSM303 (rotator_features.h)  (code contributed by AD0CZ)
    FEATURE_EL_POSITION_POLOLU_LSM303 (rotator_features.h)
    #define POLOLU_LSM_303_MIN_ARRAY {+59, +19, -731} (rotator_settings.h)
    #define POLOLU_LSM_303_MAX_ARRAY {+909, +491, +14} (rotator_settings.h)
    DEBUG_POLOLU_LSM303_CALIBRATION (rotator_features.h)

    bug fixed with brake_release() affecting elevation brake (thanks Paolo, IT9IPQ)

    LANGUAGE_ITALIAN code donated by Paolo, IT9IPQ
    OPTION_DISPLAY_VERSION_ON_STARTUP code provided by Paolo, IT9IPQ

    Fixed bug with LANGUAGE_CZECH (thanks Radek, OK2NMA)

    Change in Easycom response terminator (now uses whatever command terminator was sent to it)
    Easycom AZ EL command string response change to +xxx.xx +xxx.xx

    OPTION_HAMLIB_EASYCOM_AZ_EL_COMMAND_HACK
    LANGUAGE_PORTUGUESE_BRASIL (thanks Ismael, PY4PI)

    check_brake_release() bug fix

    configuration.az_stepper_motor_last_direction, configuration.az_stepper_motor_last_pin_state, configuration.el_stepper_motor_last_direction, configuration.el_stepper_motor_last_pin_state    
 
    OPTION_HAMLIB_EASYCOM_NO_TERMINATOR_CHARACTER_HACK ; HARDWARE_WB6KCN
    
    FEATURE_STEPPER_MOTOR_EXPERIMENTAL_CODE_2

    AZ_INCREMENTAL_ENCODER_ZERO_PULSE_POSITION
    EL_INCREMENTAL_ENCODER_ZERO_PULSE_POSITION

    OPTION_BLINK_OVERLAP_LED and OPTION_OVERLAP_LED_BLINK_MS setting

    FEATURE_SUN_PUSHBUTTON_AZ_EL_CALIBRATION and FEATURE_MOON_PUSHBUTTON_AZ_EL_CALIBRATION; pin_sun_pushbutton_calibration, pin_moon_pushbutton_calibration

    Rolled FEATURE_STEPPER_MOTOR_EXPERIMENTAL_CODE_2 into FEATURE_STEPPER_MOTOR

    Working on FEATURE_AUTOCORRECT

    Bug fix involving FEATURE_AZ_POSITION_HH12_AS5045_SSI and azimuth_offset (thanks Johan, PA3FPQ and Jurgen, PE1LWT)
    Bug fix - wasn't initializing rotate_cw_ccw (thanks Erick, WB6KCN)
    Bug fix with OPTION_DISPLAY_VERSION_ON_STARTUP and 2 row displays (thanks Gianfranco, IZ8EWD)
    OPTION_EL_PULSE_DEBOUNCE code - (thanks Gianfranco, IZ8EWD)
    #define EL_POSITION_PULSE_DEBOUNCE 500  // in ms

    OPTION_HH12_10_BIT_READINGS in hh12.h (thanks Johan PA3FPQ)

    Correction from Johan PA3FPQ on OPTION_HH12_10_BIT_READINGS

    #define BRAKE_ACTIVE_STATE HIGH
    #define BRAKE_INACTIVE_STATE LOW

    OPTION_SCANCON_2RMHF3600_INC_ENCODER - thanks Jasper, PA2J

    Fixed compile bug with OPTION_EL_MANUAL_ROTATE_LIMITS) when FEATURE_ELEVATION_CONTROL is not enabled

    Eliminated az_stepper_motor_direction and el_stepper_motor_direction.  Use rotate_* pins instead!

    Self-resetting functionality
    Fixed bug with OPTION_DISPLAY_DIRECTION_STATUS updating

    Improved the self reset with a non-blocking 5 second delay

    Added remote slave commands:
      RC - read coordinates (returns RCxx.xxxx -xxx.xxxx)
      GS - query GPS status (returns GS0 (no sync) or GS1 (sync))  

    OPTION_SYNC_MASTER_COORDINATES_TO_SLAVE 

    reset_pin

    Updated for Arduino 1.6.1

    Fixed compilation issue with FEATURE_ELEVATION_CORRECTION and Arduino 1.6.x

    OPTION_RESET_METHOD_JMP_ASM_0
    Change /E command to do setup() for system reset

    Fixed calculation issues with FEATURE_ELEVATION_CORRECTION
    Fixed bug with FEATURE_AZIMUTH_CORRECTION and FEATURE_ELEVATION_CORRECTION rounding off to nearest degree

    Change configuration.azimuth_offset to use raw_azimuth rather than azimuth for calculation

  */

#define CODE_VERSION "2.0.2015040401"

#include <avr/pgmspace.h>
#include <EEPROM.h>
#include <math.h>
#include <avr/wdt.h>

#include "rotator_hardware.h"

#ifdef HARDWARE_EA4TX_ARS_USB
#include "rotator_features_ea4tx_ars_usb.h"
#endif
#ifdef HARDWARE_WB6KCN
#include "rotator_features_wb6kcn.h"
#endif
#ifdef HARDWARE_M0UPU
#include "rotator_features_m0upu.h"
#endif
#if !defined(HARDWARE_CUSTOM)
#include "rotator_features.h"
#endif
#include "rotator_dependencies.h"


#ifdef FEATURE_4_BIT_LCD_DISPLAY
#include <LiquidCrystal.h>  // required for classic 4 bit interface LCD display (FEATURE_4_BIT_LCD_DISPLAY)
#endif // FEATURE_4_BIT_LCD_DISPLAY
#ifdef FEATURE_WIRE_SUPPORT
#include <Wire.h>  // required for FEATURE_I2C_LCD, any ADXL345 feature, FEATURE_AZ_POSITION_HMC5883L, FEATURE_EL_POSITION_ADAFRUIT_LSM303
#endif
#if defined(FEATURE_ADAFRUIT_I2C_LCD)
#include <Adafruit_MCP23017.h> // required for Adafruit I2C LCD display
#include <Adafruit_RGBLCDShield.h> // required for Adafruit I2C LCD display
#endif
#if defined(FEATURE_YOURDUINO_I2C_LCD) || defined(FEATURE_RFROBOT_I2C_DISPLAY)
#include <LiquidCrystal_I2C.h> // required for YourDuino.com or DFRobot I2C LCD display
#endif
#if defined(FEATURE_YOURDUINO_I2C_LCD)
#include <LCD.h>   // required for YourDuino.com I2C LCD display
#endif
#if defined(FEATURE_AZ_POSITION_HMC5883L)
#include <HMC5883L.h> // required for HMC5883L digital compass support
#endif
#if defined(FEATURE_EL_POSITION_ADXL345_USING_ADAFRUIT_LIB) || defined(FEATURE_AZ_POSITION_ADAFRUIT_LSM303) || defined(FEATURE_EL_POSITION_ADAFRUIT_LSM303)
#include <Adafruit_Sensor.h>    // required for any Adafruit sensor libraries
#endif
#if defined(FEATURE_EL_POSITION_ADXL345_USING_LOVE_ELECTRON_LIB)
#include <ADXL345.h>  // required for elevation ADXL345 accelerometer using Love Electronics ADXL345 library
#endif
#if defined(FEATURE_EL_POSITION_ADXL345_USING_ADAFRUIT_LIB)
#include <Adafruit_ADXL345_U.h>   // required for elevation ADXL345 accelerometer using Adafruit ADXL345 library (FEATURE_EL_POSITION_ADXL345_USING_ADAFRUIT_LIB)
#endif
#if defined(FEATURE_EL_POSITION_ADAFRUIT_LSM303) || defined(FEATURE_AZ_POSITION_ADAFRUIT_LSM303)
#include <Adafruit_LSM303.h>     // required for azimuth and/or elevation using LSM303 compass and/or accelerometer
#endif
#ifdef FEATURE_AZ_POSITION_POLOLU_LSM303
#include <LSM303.h>
#endif
#if defined(FEATURE_MOON_TRACKING) || defined(FEATURE_SUN_TRACKING)
#include "moon2.h"
#endif // defined(FEATURE_MOON_TRACKING) || defined(FEATURE_SUN_TRACKING)
#ifdef FEATURE_SUN_TRACKING
#include "sunpos.h"
#endif // FEATURE_SUN_TRACKING
#ifdef FEATURE_GPS
#include "TinyGPS.h"
#endif // FEATURE_GPS
#ifdef FEATURE_RTC_DS1307
#include "RTClib.h"
#endif // FEATURE_RTC_DS1307
#ifdef FEATURE_RTC_PCF8583
#include "PCF8583.h"
#endif //FEATURE_RTC_PCF8583
#ifdef FEATURE_ETHERNET
#include "SPI.h"
#include "Ethernet.h"
#endif


#include "rotator.h"

#ifdef HARDWARE_EA4TX_ARS_USB
#include "rotator_pins_ea4tx_ars_usb.h"
#endif
#ifdef HARDWARE_M0UPU
#include "rotator_pins_m0upu.h"
#endif
#ifdef HARDWARE_WB6KCN
//#include "rotator_pins_wb6kcn_az_test_setup.h"
#include "rotator_pins_wb6kcn.h"
#endif
#if !defined(HARDWARE_CUSTOM)
#include "rotator_pins.h"
#endif

#ifdef HARDWARE_EA4TX_ARS_USB
#include "rotator_settings_ea4tx_ars_usb.h"
#endif
#ifdef HARDWARE_WB6KCN
#include "rotator_settings_wb6kcn.h"
#endif
#ifdef HARDWARE_M0UPU
#include "rotator_settings_m0upu.h"
#endif
#if !defined(HARDWARE_CUSTOM)
#include "rotator_settings.h"
#endif

#ifdef FEATURE_STEPPER_MOTOR
#include "TimerFive.h"
#endif





/*----------------------- variables -------------------------------------*/

byte incoming_serial_byte = 0;

byte reset_the_unit = 0;

#ifdef FEATURE_TWO_DECIMAL_PLACE_HEADINGS
long azimuth = 0;
long raw_azimuth = 0;
long target_azimuth = 0;
long target_raw_azimuth = 0;
long azimuth_starting_point = AZIMUTH_STARTING_POINT_DEFAULT;
long azimuth_rotation_capability = AZIMUTH_ROTATION_CAPABILITY_DEFAULT;
#else
int azimuth = 0;
int raw_azimuth = 0;
int target_azimuth = 0;
int target_raw_azimuth = 0;
int azimuth_starting_point = AZIMUTH_STARTING_POINT_DEFAULT;
int azimuth_rotation_capability = AZIMUTH_ROTATION_CAPABILITY_DEFAULT;
#endif




byte control_port_buffer[COMMAND_BUFFER_SIZE];
int control_port_buffer_index = 0;
byte az_state = IDLE;
byte debug_mode = DEFAULT_DEBUG_STATE;
int analog_az = 0;
unsigned long last_debug_output_time = 0;
unsigned long az_last_rotate_initiation = 0;
byte azimuth_button_was_pushed = 0;
byte brake_az_engaged = 0;
byte brake_el_engaged = 0;
byte configuration_dirty = 0;
unsigned long last_serial_receive_time = 0;

byte az_slowstart_active = AZ_SLOWSTART_DEFAULT;
byte az_slowdown_active = AZ_SLOWDOWN_DEFAULT;

byte az_request = 0;
int az_request_parm = 0;
byte az_request_queue_state = NONE;

unsigned long az_slowstart_start_time = 0;
byte az_slow_start_step = 0;
unsigned long az_last_step_time = 0;
byte az_slow_down_step = 0;
unsigned long az_timed_slow_down_start_time = 0;
byte backslash_command = 0;

struct config_t {
  byte magic_number;
  int analog_az_full_ccw;
  int analog_az_full_cw;
  int analog_el_0_degrees;
  int analog_el_max_elevation;
  // int azimuth_starting_point;
  // int azimuth_rotation_capability;
  float last_azimuth;
  float last_elevation;
  int last_az_incremental_encoder_position;
  int last_el_incremental_encoder_position;
  float azimuth_offset;
  float elevation_offset;
  byte az_stepper_motor_last_pin_state;
  byte el_stepper_motor_last_pin_state;
  byte az_stepper_motor_last_direction;
  byte el_stepper_motor_last_direction;
} configuration;




#ifdef FEATURE_TIMED_BUFFER
int timed_buffer_azimuths[TIMED_INTERVAL_ARRAY_SIZE];
int timed_buffer_number_entries_loaded = 0;
int timed_buffer_entry_pointer = 0;
int timed_buffer_interval_value_seconds = 0;
unsigned long last_timed_buffer_action_time = 0;
byte timed_buffer_status = 0;
#endif // FEATURE_TIMED_BUFFER

byte normal_az_speed_voltage = 0;
byte current_az_speed_voltage = 0;

#ifdef FEATURE_ELEVATION_CONTROL
int elevation = 0;
int target_elevation = 0;

byte el_request = 0;
int el_request_parm = 0;
byte el_request_queue_state = NONE;
byte el_slowstart_active = EL_SLOWSTART_DEFAULT;
byte el_slowdown_active = EL_SLOWDOWN_DEFAULT;
unsigned long el_slowstart_start_time = 0;
byte el_slow_start_step = 0;
unsigned long el_last_step_time = 0;
byte el_slow_down_step = 0;
unsigned long el_timed_slow_down_start_time = 0;
byte normal_el_speed_voltage = 0;
byte current_el_speed_voltage = 0;

int display_elevation = 0;
byte el_state = IDLE;
int analog_el = 0;

unsigned long el_last_rotate_initiation = 0;
#ifdef FEATURE_TIMED_BUFFER
int timed_buffer_elevations[TIMED_INTERVAL_ARRAY_SIZE];
#endif // FEATURE_TIMED_BUFFER
byte elevation_button_was_pushed = 0;
#endif // FEATURE_ELEVATION_CONTROL

#ifdef FEATURE_LCD_DISPLAY
unsigned long last_lcd_update = 0;
String last_row_0_string;  // this is only used in update_display(), however if you make it a static within the funtion, the compiler errors out with a strange error
byte push_lcd_update = 0;
#endif // FEATURE_LCD_DISPLAY

#ifdef FEATURE_ROTARY_ENCODER_SUPPORT
#ifdef OPTION_ENCODER_HALF_STEP_MODE      // Use the half-step state table (emits a code at 00 and 11)
const unsigned char ttable[6][4] = {
  { 0x3,  0x2,  0x1, 0x0  }, { 0x23, 0x0,  0x1, 0x0  },
  { 0x13, 0x2,  0x0, 0x0  }, { 0x3,  0x5,  0x4, 0x0  },
  { 0x3,  0x3,  0x4, 0x10 }, { 0x3,  0x5,  0x3, 0x20 }
};
#else                                      // Use the full-step state table (emits a code at 00 only)
const unsigned char ttable[7][4] = {
  { 0x0, 0x2, 0x4, 0x0  }, { 0x3, 0x0, 0x1, 0x10 },
  { 0x3, 0x2, 0x0, 0x0  }, { 0x3, 0x2, 0x1, 0x0  },
  { 0x6, 0x0, 0x4, 0x0  }, { 0x6, 0x5, 0x0, 0x10 },
  { 0x6, 0x5, 0x4, 0x0  },
};
#endif // OPTION_ENCODER_HALF_STEP_MODE
#ifdef FEATURE_AZ_PRESET_ENCODER            // Rotary Encoder State Tables
#if defined(FEATURE_ONE_DECIMAL_PLACE_HEADINGS) || defined(FEATURE_TWO_DECIMAL_PLACE_HEADINGS)
double az_encoder_raw_degrees = 0;
#else
int az_encoder_raw_degrees = 0;
#endif
volatile unsigned char az_encoder_state = 0;
#ifdef FEATURE_EL_PRESET_ENCODER
volatile unsigned char el_encoder_state = 0;
#if defined(FEATURE_ONE_DECIMAL_PLACE_HEADINGS) || defined(FEATURE_TWO_DECIMAL_PLACE_HEADINGS)
double el_encoder_degrees = 0;
#else
int el_encoder_degrees = 0;
#endif
#endif // FEATURE_EL_PRESET_ENCODER
byte preset_encoders_state = ENCODER_IDLE;
#endif // FEATURE_AZ_PRESET_ENCODER
#endif // FEATURE_ROTARY_ENCODER_SUPPORT

#ifdef DEBUG_PROFILE_LOOP_TIME
float average_loop_time = 0;
#endif // DEBUG_PROFILE_LOOP_TIME

#ifdef FEATURE_AZ_POSITION_PULSE_INPUT
volatile float az_position_pulse_input_azimuth = 0;
volatile byte last_known_az_state = 0;
#endif // FEATURE_AZ_POSITION_PULSE_INPUT

#ifdef FEATURE_EL_POSITION_PULSE_INPUT
volatile float el_position_pulse_input_elevation = 0;
volatile byte last_known_el_state = 0;
#ifdef OPTION_EL_PULSE_DEBOUNCE
unsigned long last_el_pulse_debounce = 0;
#endif //OPTION_EL_PULSE_DEBOUNCE

#endif // FEATURE_EL_POSITION_PULSE_INPUT

#if defined(FEATURE_REMOTE_UNIT_SLAVE) || defined(FEATURE_MASTER_WITH_SERIAL_SLAVE) 
byte serial_read_event_flag[] = { 0, 0, 0, 0, 0 };
byte control_port_buffer_carriage_return_flag = 0;
#endif

#if defined(FEATURE_MASTER_WITH_SERIAL_SLAVE) || defined(FEATURE_MASTER_WITH_ETHERNET_SLAVE)
byte remote_unit_port_buffer[COMMAND_BUFFER_SIZE];
int remote_unit_port_buffer_index = 0;
byte remote_unit_port_buffer_carriage_return_flag = 0;
unsigned long serial1_last_receive_time = 0;
byte remote_unit_command_submitted = 0;
unsigned long last_remote_unit_command_time = 0;
unsigned int remote_unit_command_timeouts = 0;
unsigned int remote_unit_bad_results = 0;
unsigned long remote_unit_good_results = 0;
unsigned int remote_unit_incoming_buffer_timeouts = 0;
byte remote_unit_command_results_available = 0;
float remote_unit_command_result_float = 0;
byte remote_port_rx_sniff = 0;
byte remote_port_tx_sniff = 0;
byte suspend_remote_commands = 0;
#if defined(OPTION_SYNC_MASTER_CLOCK_TO_SLAVE) && defined(FEATURE_CLOCK)
byte clock_synced_to_remote = 0;
#endif
#endif //FEATURE_MASTER_WITH_SERIAL_SLAVE


#ifdef DEBUG_POSITION_PULSE_INPUT
// unsigned int az_position_pule_interrupt_handler_flag = 0;
// unsigned int el_position_pule_interrupt_handler_flag = 0;
volatile unsigned long az_pulse_counter = 0;
volatile unsigned long el_pulse_counter = 0;
volatile unsigned int az_pulse_counter_ambiguous = 0;
volatile unsigned int el_pulse_counter_ambiguous = 0;
#endif // DEBUG_POSITION_PULSE_INPUT

#ifdef FEATURE_PARK
byte park_status = NOT_PARKED;
byte park_serial_initiated = 0;
#endif // FEATURE_PARK

#ifdef FEATURE_AZ_POSITION_INCREMENTAL_ENCODER
volatile int az_incremental_encoder_position = 0;
volatile byte az_3_phase_encoder_last_phase_a_state = 0;
volatile byte az_3_phase_encoder_last_phase_b_state = 0;
#ifdef DEBUG_AZ_POSITION_INCREMENTAL_ENCODER
volatile int az_position_incremental_encoder_interrupt = 0;
#endif // DEBUG_AZ_POSITION_INCREMENTAL_ENCODER
#endif // FEATURE_AZ_POSITION_INCREMENTAL_ENCODER

#ifdef FEATURE_EL_POSITION_INCREMENTAL_ENCODER
volatile int el_incremental_encoder_position = 0;
volatile byte el_3_phase_encoder_last_phase_a_state = 0;
volatile byte el_3_phase_encoder_last_phase_b_state = 0;
#ifdef DEBUG_EL_POSITION_INCREMENTAL_ENCODER
volatile int el_position_incremental_encoder_interrupt = 0;
#endif // DEBUG_EL_POSITION_INCREMENTAL_ENCODER
#endif // FEATURE_EL_POSITION_INCREMENTAL_ENCODER

#ifdef FEATURE_AZ_POSITION_INCREMENTAL_ENCODER
volatile byte read_azimuth_lock = 0;
#endif

#ifdef FEATURE_EL_POSITION_INCREMENTAL_ENCODER
volatile byte read_elevation_lock = 0;
#endif

#if defined(FEATURE_AZ_POSITION_INCREMENTAL_ENCODER) || defined(FEATURE_EL_POSITION_INCREMENTAL_ENCODER)
volatile byte service_rotation_lock = 0;
#endif

#if defined(FEATURE_REMOTE_UNIT_SLAVE) || defined(FEATURE_YAESU_EMULATION) || defined(FEATURE_EASYCOM_EMULATION) || defined(FEATURE_CLOCK)
HardwareSerial * control_port;
#endif

#if defined(FEATURE_MASTER_WITH_SERIAL_SLAVE)
HardwareSerial * remote_unit_port;
#endif

#if defined(FEATURE_GPS)
HardwareSerial * gps_port;
#ifdef GPS_MIRROR_PORT
HardwareSerial * (gps_mirror_port);
#endif //GPS_MIRROR_PORT
#endif //defined(FEATURE_GPS)

#if defined(FEATURE_MOON_TRACKING) || defined(FEATURE_SUN_TRACKING) || defined(FEATURE_CLOCK) || (defined(FEATURE_GPS) && defined(FEATURE_REMOTE_UNIT_SLAVE))
double latitude = DEFAULT_LATITUDE;
double longitude = DEFAULT_LONGITUDE;
#endif


#ifdef FEATURE_MOON_TRACKING
byte moon_tracking_active = 0;
byte moon_visible = 0;
double moon_azimuth = 0;
double moon_elevation = 0;
#endif // FEATURE_MOON_TRACKING

#ifdef FEATURE_SUN_TRACKING
float sun_azimuth = 0;
float sun_elevation = 0;
cTime c_time;
cLocation c_loc;
cSunCoordinates c_sposn;
byte sun_visible = 0;
byte sun_tracking_active = 0;
#endif // FEATURE_SUN_TRACKING

#ifdef FEATURE_CLOCK
unsigned long clock_years = 0;
unsigned long clock_months = 0;
unsigned long clock_days = 0;
unsigned long clock_hours = 0;
unsigned long clock_minutes = 0;
unsigned long clock_seconds = 0;
int clock_year_set = 2014;
byte clock_month_set = 1;
byte clock_day_set = 1;
byte clock_sec_set = 0;
unsigned long clock_hour_set = 0;
unsigned long clock_min_set = 0;
unsigned long millis_at_last_calibration = 0;
#endif // FEATURE_CLOCK

#if defined(FEATURE_GPS) || defined(FEATURE_RTC) || defined(FEATURE_CLOCK)
byte clock_status = FREE_RUNNING;
#endif // defined(FEATURE_GPS) || defined(FEATURE_RTC)

#ifdef FEATURE_GPS
byte gps_data_available = 0;
#endif // FEATURE_GPS

#ifdef FEATURE_ETHERNET
byte mac[] = {ETHERNET_MAC_ADDRESS};
IPAddress ip(ETHERNET_IP_ADDRESS);
IPAddress gateway(ETHERNET_IP_GATEWAY);
IPAddress subnet(ETHERNET_IP_SUBNET_MASK);
EthernetClient ethernetclient0;
EthernetServer ethernetserver0(ETHERNET_TCP_PORT_0);
#ifdef ETHERNET_TCP_PORT_1
EthernetClient ethernetclient1;
EthernetServer ethernetserver1(ETHERNET_TCP_PORT_1);
#endif //ETHERNET_TCP_PORT_1
#ifdef FEATURE_MASTER_WITH_ETHERNET_SLAVE
EthernetClient ethernetslavelinkclient0;
IPAddress slave_unit_ip(ETHERNET_SLAVE_IP_ADDRESS);
byte ethernetslavelinkclient0_state = ETHERNET_SLAVE_DISCONNECTED;
unsigned int ethernet_slave_reconnects = 0;
#endif //FEATURE_MASTER_WITH_ETHERNET_SLAVE
#endif //FEATURE_ETHERNET

#ifdef FEATURE_POWER_SWITCH
unsigned long last_activity_time = 0;
#endif //FEATURE_POWER_SWITCH

#ifdef FEATURE_STEPPER_MOTOR
volatile unsigned int az_stepper_freq_count = 0;
#ifdef FEATURE_ELEVATION_CONTROL
volatile unsigned int el_stepper_freq_count = 0;
#endif //FEATURE_ELEVATION_CONTROL
volatile unsigned long service_stepper_motor_pulse_pins_count = 0;
#endif //FEATURE_STEPPER_MOTOR

#ifdef FEATURE_AZIMUTH_CORRECTION
float azimuth_calibration_from[]  = AZIMUTH_CALIBRATION_FROM_ARRAY;    
float azimuth_calibration_to[]    = AZIMUTH_CALIBRATION_TO_ARRAY;
#endif // FEATURE_AZIMUTH_CORRECTION

#ifdef FEATURE_ELEVATION_CORRECTION
float elevation_calibration_from[]  = ELEVATION_CALIBRATION_FROM_ARRAY;
float elevation_calibration_to[]    = ELEVATION_CALIBRATION_TO_ARRAY;
#endif // FEATURE_ELEVATION_CORRECTION

#ifdef FEATURE_AUTOCORRECT
byte autocorrect_state_az = AUTOCORRECT_INACTIVE;
float autocorrect_az = 0;
unsigned long autocorrect_az_submit_time = 0;
#ifdef FEATURE_ELEVATION_CONTROL
byte autocorrect_state_el = AUTOCORRECT_INACTIVE;
float autocorrect_el = 0;
unsigned long autocorrect_el_submit_time = 0;
#endif //FEATURE_ELEVATION_CONTROL
#endif //FEATURE_AUTOCORRECT

/* ------------------ let's start doing some stuff now that we got the formalities out of the way --------------------*/

void setup() {

//  delay(1000);
//added Craig Benson UNSW@ADFA 2015-05-01 to get code to do something...
//  Serial.begin(9600);
//    Serial.println("Hello World in setup\n");
//    delay(1000);
// end added 2015-05-01
  initialize_serial();
//added Craig Benson UNSW@ADFA 2015-05-01 to get code to do something...
//  Serial.begin(9600);
//    Serial.println("Hello World in setup after initialize serial\n");
//    delay(1000);
// end added 2015-05-01
  initialize_peripherals();

  read_settings_from_eeprom();
//added Craig Benson UNSW@ADFA 2015-05-01 to get code to do something...
//  Serial.begin(9600);
//    Serial.println("Hello World in setup b4 pins \n");
//    delay(1000);
// end added 2015-05-01
  initialize_pins();

  read_azimuth(0);
  #ifdef FEATURE_YAESU_EMULATION
  //report_current_azimuth();      // Yaesu - report the azimuth right off the bat without a C command; the Arduino doesn't wake up quick enough
                                 // to get first C command from HRD and if HRD doesn't see anything it doesn't connect
  #endif // FEATURE_YAESU_EMULATION

  #ifdef FEATURE_TIMED_BUFFER
  timed_buffer_status = EMPTY;
  #endif // FEATURE_TIMED_BUFFER

  #ifdef FEATURE_LCD_DISPLAY
  initialize_display();
  #endif

  initialize_rotary_encoders();

  initialize_interrupts();


} /* setup */

/*-------------------------- here's where the magic happens --------------------------------*/

void loop() {
  //added Craig Benson UNSW@ADFA 2015-05-01 to get code to do something...
//Serial.begin(9600);
//Serial.println("Hello World starting loop\n");
//delay(1000);
//end added 2015-05-01
  check_serial();
      //added Craig Benson UNSW@ADFA 2015-05-01 to get code to do something...
//Serial.begin(9600);
//Serial.println("Hello World  loop after check serial \n");//read_headings\n");
//delay(1000);
//end added 2015-05-01
  read_headings();
  


  #ifndef FEATURE_REMOTE_UNIT_SLAVE
  service_request_queue();
  service_rotation();
  az_check_operation_timeout();
  #ifdef FEATURE_TIMED_BUFFER
  check_timed_interval();
  #endif // FEATURE_TIMED_BUFFER
  read_headings();
  check_buttons();
  check_overlap();
  check_brake_release();
  #ifdef FEATURE_ELEVATION_CONTROL
  el_check_operation_timeout();
  #endif
  #endif // ndef FEATURE_REMOTE_UNIT_SLAVE

  //read_headings();

  #ifdef FEATURE_LCD_DISPLAY
  update_display();
  #endif


  #ifndef FEATURE_REMOTE_UNIT_SLAVE
  #ifdef OPTION_AZ_MANUAL_ROTATE_LIMITS
  check_az_manual_rotate_limit();
  #endif


  #if defined(OPTION_EL_MANUAL_ROTATE_LIMITS) && defined(FEATURE_ELEVATION_CONTROL)
  check_el_manual_rotate_limit();
  #endif

  check_az_speed_pot();


  #ifdef FEATURE_AZ_PRESET_ENCODER            // Rotary Encoder or Preset Selector
  check_preset_encoders();
  #else
  check_az_preset_potentiometer();
  #endif // FEATURE_AZ_PRESET_ENCODER
  #endif // ndef FEATURE_REMOTE_UNIT_SLAVE

  #ifdef DEBUG_DUMP
  output_debug();
  #endif //DEBUG_DUMP
  //added Craig Benson UNSW@ADFA 2015-05-01 to get code to do something...
//Serial.begin(9600);
//Serial.println("Hello World  loop before read_headings()\n");//after check_az_speed_pot\n");
//delay(1000);
//end added 2015-05-01
  read_headings();

  #ifndef FEATURE_REMOTE_UNIT_SLAVE
  service_rotation();
  #endif  

  check_for_dirty_configuration();



  #ifdef DEBUG_PROFILE_LOOP_TIME
  profile_loop_time();
  #endif //DEBUG_PROFILE_LOOP_TIME

  #ifdef FEATURE_REMOTE_UNIT_SLAVE
  service_remote_unit_serial_buffer();
  #endif // FEATURE_REMOTE_UNIT_SLAVE

  #if defined(FEATURE_MASTER_WITH_SERIAL_SLAVE) || defined(FEATURE_MASTER_WITH_ETHERNET_SLAVE)
  service_remote_communications_incoming_buffer();
  #endif // defined(FEATURE_MASTER_WITH_SERIAL_SLAVE) || defined(FEATURE_MASTER_WITH_ETHERNET_SLAVE)

  #ifdef FEATURE_JOYSTICK_CONTROL
  check_joystick();
  #endif // FEATURE_JOYSTICK_CONTROL

  #ifdef FEATURE_ROTATION_INDICATOR_PIN
  service_rotation_indicator_pin();
  #endif // FEATURE_ROTATION_INDICATOR_PIN

  #ifdef FEATURE_PARK
  service_park();
  #endif // FEATURE_PARK

  #ifdef FEATURE_LIMIT_SENSE
  check_limit_sense();
  #endif // FEATURE_LIMIT_SENSE

  #ifdef FEATURE_MOON_TRACKING
  service_moon_tracking();
  #endif // FEATURE_MOON_TRACKING

  #ifdef FEATURE_SUN_TRACKING
  service_sun_tracking();
  #endif // FEATURE_SUN_TRACKING

  #ifdef FEATURE_GPS
  service_gps();
  #endif // FEATURE_GPS

  read_headings();

  #ifndef FEATURE_REMOTE_UNIT_SLAVE
  service_rotation();
  #endif  

  #ifdef FEATURE_RTC
  service_rtc();
  #endif // FEATURE_RTC

  #ifdef FEATURE_ETHERNET
  service_ethernet();
  #endif // FEATURE_ETHERNET

  #ifdef FEATURE_POWER_SWITCH
  service_power_switch();
  #endif //FEATURE_POWER_SWITCH

  #if defined(FEATURE_CLOCK) && defined(OPTION_SYNC_MASTER_CLOCK_TO_SLAVE) && (defined(FEATURE_MASTER_WITH_SERIAL_SLAVE) || defined(FEATURE_MASTER_WITH_ETHERNET_SLAVE))
  sync_master_clock_to_slave();
  #endif

  #if defined(OPTION_SYNC_MASTER_COORDINATES_TO_SLAVE) && (defined(FEATURE_MASTER_WITH_SERIAL_SLAVE) || defined(FEATURE_MASTER_WITH_ETHERNET_SLAVE))
  sync_master_coordinates_to_slave();
  #endif


  service_blink_led();

  #ifdef FEATURE_ANALOG_OUTPUT_PINS
  service_analog_output_pins();
  #endif //FEATURE_ANALOG_OUTPUT_PINS


  #if defined(FEATURE_SUN_PUSHBUTTON_AZ_EL_CALIBRATION) && defined(FEATURE_SUN_TRACKING)
  check_sun_pushbutton_calibration();
  #endif //defined(FEATURE_SUN_PUSHBUTTON_AZ_EL_CALIBRATION) && defined(FEATURE_SUN_TRACKING)

  #if defined(FEATURE_MOON_PUSHBUTTON_AZ_EL_CALIBRATION) && defined(FEATURE_MOON_TRACKING)
  check_moon_pushbutton_calibration();
  #endif //defined(FEATURE_MOON_PUSHBUTTON_AZ_EL_CALIBRATION) && defined(FEATURE_MOON_TRACKING)

  check_for_reset_flag();

} /* loop */
/* -------------------------------------- subroutines -----------------------------------------------
 
                                  Where the real work happens...
 
 */

void read_headings(){

  read_azimuth(0);

  #ifdef FEATURE_ELEVATION_CONTROL
  read_elevation(0);
  #endif

}

// --------------------------------------------------------------

void service_blink_led(){


  #ifdef blink_led
  static unsigned long last_blink_led_transition = 0;
  static byte blink_led_status = 0;


  if (((millis() - last_blink_led_transition) >= 1000) && (blink_led != 0)) {
    if (blink_led_status) {
      digitalWriteEnhanced(blink_led, LOW);
      blink_led_status = 0;
    } else {
      digitalWriteEnhanced(blink_led, HIGH);
      blink_led_status = 1;
    }
    last_blink_led_transition = millis();
  }
  #endif // blink_led

  


} /* service_blink_led */


// --------------------------------------------------------------
void check_for_reset_flag(){

  static unsigned long detected_reset_flag_time = 0;

  if (reset_the_unit){
    if (detected_reset_flag_time == 0){
      detected_reset_flag_time = millis();
    } else {
      if ((millis()-detected_reset_flag_time) > 5000){  // let things run for 5 seconds


        #ifdef reset_pin
        digitalWrite(reset_pin,HIGH);
        #else // reset_pin

        #ifdef OPTION_RESET_METHOD_JMP_ASM_0
        asm volatile ("  jmp 0"); // reboot!     // doesn't work on Arduino Mega but works on SainSmart Mega.
        //wdt_enable(WDTO_30MS); while(1) {};  //doesn't work on Mega
        #else //OPTION_RESET_METHOD_JMP_ASM_0
        setup();
        reset_the_unit = 0;
        #endif //OPTION_RESET_METHOD_JMP_ASM_0
        
        #endif //reset_pin
      }
    }
  }

}

// --------------------------------------------------------------
#ifdef DEBUG_PROFILE_LOOP_TIME
void profile_loop_time(){

  static unsigned long last_time = 0;
  static unsigned long last_print_time = 0;


  average_loop_time = (average_loop_time + (millis() - last_time)) / 2.0;
  last_time = millis();

  if (debug_mode) {
    if ((millis() - last_print_time) > 1000) {
      debug_print("avg loop time: ");
      debug_print_float(average_loop_time, 2);
      debug_println("");
      last_print_time = millis();
    }
  }


} /* profile_loop_time */

#endif //DEBUG_PROFILE_LOOP_TIME
// --------------------------------------------------------------
void check_az_speed_pot() {

  static unsigned long last_pot_check_time = 0;
  int pot_read = 0;
  byte new_azimuth_speed_voltage = 0;


  if (az_speed_pot /*&& azimuth_speed_voltage*/ && ((millis() - last_pot_check_time) > 500)) {
    pot_read = analogReadEnhanced(az_speed_pot);
    new_azimuth_speed_voltage = map(pot_read, SPEED_POT_LOW, SPEED_POT_HIGH, SPEED_POT_LOW_MAP, SPEED_POT_HIGH_MAP);
    if (new_azimuth_speed_voltage != normal_az_speed_voltage) {
      #ifdef DEBUG_AZ_SPEED_POT
      if (debug_mode) {
        debug_print("check_az_speed_pot: normal_az_speed_voltage: ");
        debug_print_int(normal_az_speed_voltage);
        debug_print(" new_azimuth_speed_voltage:");
        debug_print_int(new_azimuth_speed_voltage);
        debug_println("");
      }
      #endif // DEBUG_AZ_SPEED_POT
      normal_az_speed_voltage = new_azimuth_speed_voltage;
      update_az_variable_outputs(normal_az_speed_voltage);
      #if defined(OPTION_EL_SPEED_FOLLOWS_AZ_SPEED) && defined(FEATURE_ELEVATION_CONTROL)
      normal_el_speed_voltage = new_azimuth_speed_voltage;
      update_el_variable_outputs(normal_el_speed_voltage);
      #endif // OPTION_EL_SPEED_FOLLOWS_AZ_SPEED
    }
    last_pot_check_time = millis();
  }

} /* check_az_speed_pot */
// --------------------------------------------------------------
void check_az_preset_potentiometer() {


  byte check_pot = 0;
  static unsigned long last_pot_check_time = 0;
  static int last_pot_read = 9999;
  int pot_read = 0;
  int new_pot_azimuth = 0;
  byte button_read = 0;
  static byte pot_changed_waiting = 0;

  if (az_preset_pot) {
    if (last_pot_read == 9999) {  // initialize last_pot_read the first time we hit this subroutine
      last_pot_read = analogReadEnhanced(az_preset_pot);
    }

    if (!pot_changed_waiting) {
      if (preset_start_button) { // if we have a preset start button, check it
        button_read = digitalReadEnhanced(preset_start_button);
        if (button_read == BUTTON_ACTIVE_STATE) {
          check_pot = 1;
        }
      } else {  // if not, check the pot every 500 mS
        if ((millis() - last_pot_check_time) < 250) {
          check_pot = 1;
        }
      }
      if (check_pot) {
        pot_read = analogReadEnhanced(az_preset_pot);
        new_pot_azimuth = map(pot_read, AZ_PRESET_POT_FULL_CW, AZ_PRESET_POT_FULL_CCW, AZ_PRESET_POT_FULL_CW_MAP, AZ_PRESET_POT_FULL_CCW_MAP);
        if ((abs(last_pot_read - pot_read) > 4) && (abs(new_pot_azimuth - (raw_azimuth / HEADING_MULTIPLIER)) > AZIMUTH_TOLERANCE)) {
          pot_changed_waiting = 1;
          #ifdef DEBUG_AZ_PRESET_POT
          if (debug_mode) {
            debug_println("check_az_preset_potentiometer: in pot_changed_waiting");
          }
          #endif // DEBUG_AZ_PRESET_POT
          last_pot_read = pot_read;
        }
      }
      last_pot_check_time = millis();
    } else {  // we're in pot change mode
      pot_read = analogReadEnhanced(az_preset_pot);
      if (abs(pot_read - last_pot_read) > 3) {  // if the pot has changed, reset the timer
        last_pot_check_time = millis();
        last_pot_read = pot_read;
      } else {
        if ((millis() - last_pot_check_time) >= 250) {  // has it been awhile since the last pot change?
          new_pot_azimuth = map(pot_read, AZ_PRESET_POT_FULL_CW, AZ_PRESET_POT_FULL_CCW, AZ_PRESET_POT_FULL_CW_MAP, AZ_PRESET_POT_FULL_CCW_MAP);
          #ifdef DEBUG_AZ_PRESET_POT
          if (debug_mode) {
            debug_print("check_az_preset_potentiometer: pot change - current raw_azimuth: ");
            debug_print_float(raw_azimuth / HEADING_MULTIPLIER,0);
            debug_print(" new_azimuth: ");
            debug_print_int(new_pot_azimuth);
            debug_println("");
          }
          #endif // DEBUG_AZ_PRESET_POT
          submit_request(AZ, REQUEST_AZIMUTH_RAW, new_pot_azimuth * HEADING_MULTIPLIER, 44);
          pot_changed_waiting = 0;
          last_pot_read = pot_read;
          last_pot_check_time = millis();
        }
      }
    }
  } // if (az_preset_pot)
} /* check_az_preset_potentiometer */
// --------------------------------------------------------------

void initialize_rotary_encoders(){

  #ifdef FEATURE_AZ_PRESET_ENCODER
  pinModeEnhanced(az_rotary_preset_pin1, INPUT);
  pinModeEnhanced(az_rotary_preset_pin2, INPUT);
  az_encoder_raw_degrees = raw_azimuth;
  #ifdef OPTION_ENCODER_ENABLE_PULLUPS
  digitalWriteEnhanced(az_rotary_preset_pin1, HIGH);
  digitalWriteEnhanced(az_rotary_preset_pin2, HIGH);
  #endif // OPTION_ENCODER_ENABLE_PULLUPS
  #endif // FEATURE_AZ_PRESET_ENCODER


  #ifdef FEATURE_EL_PRESET_ENCODER
  pinModeEnhanced(el_rotary_preset_pin1, INPUT);
  pinModeEnhanced(el_rotary_preset_pin2, INPUT);
  el_encoder_degrees = elevation;
  #ifdef OPTION_ENCODER_ENABLE_PULLUPS
  digitalWriteEnhanced(el_rotary_preset_pin1, HIGH);
  digitalWriteEnhanced(el_rotary_preset_pin2, HIGH);
  #endif // OPTION_ENCODER_ENABLE_PULLUPS
  #endif // FEATURE_EL_PRESET_ENCODER

  #ifdef FEATURE_AZ_POSITION_ROTARY_ENCODER
  pinModeEnhanced(az_rotary_position_pin1, INPUT);
  pinModeEnhanced(az_rotary_position_pin2, INPUT);
  #ifdef OPTION_ENCODER_ENABLE_PULLUPS
  digitalWriteEnhanced(az_rotary_position_pin1, HIGH);
  digitalWriteEnhanced(az_rotary_position_pin2, HIGH);
  #endif // OPTION_ENCODER_ENABLE_PULLUPS
  #endif // FEATURE_AZ_POSITION_ROTARY_ENCODER


  #ifdef FEATURE_EL_POSITION_ROTARY_ENCODER
  pinModeEnhanced(el_rotary_position_pin1, INPUT);
  pinModeEnhanced(el_rotary_position_pin2, INPUT);
  #ifdef OPTION_ENCODER_ENABLE_PULLUPS
  digitalWriteEnhanced(el_rotary_position_pin1, HIGH);
  digitalWriteEnhanced(el_rotary_position_pin2, HIGH);
  #endif // OPTION_ENCODER_ENABLE_PULLUPS
  #endif // FEATURE_EL_POSITION_ROTARY_ENCODER


  #ifdef FEATURE_AZ_POSITION_INCREMENTAL_ENCODER
  pinMode(az_incremental_encoder_pin_phase_a, INPUT);
  pinMode(az_incremental_encoder_pin_phase_b, INPUT);
  pinMode(az_incremental_encoder_pin_phase_z, INPUT);
  #ifdef OPTION_INCREMENTAL_ENCODER_PULLUPS
  digitalWrite(az_incremental_encoder_pin_phase_a, HIGH);
  digitalWrite(az_incremental_encoder_pin_phase_b, HIGH);
  digitalWrite(az_incremental_encoder_pin_phase_z, HIGH);
  #endif // OPTION_INCREMENTAL_ENCODER_PULLUPS
  attachInterrupt(AZ_POSITION_INCREMENTAL_ENCODER_A_PIN_INTERRUPT, az_position_incremental_encoder_interrupt_handler, CHANGE);
  attachInterrupt(AZ_POSITION_INCREMENTAL_ENCODER_B_PIN_INTERRUPT, az_position_incremental_encoder_interrupt_handler, CHANGE);
  delay(250);
  az_3_phase_encoder_last_phase_a_state = digitalRead(az_incremental_encoder_pin_phase_a);
  az_3_phase_encoder_last_phase_b_state = digitalRead(az_incremental_encoder_pin_phase_b);
  #endif // FEATURE_AZ_POSITION_INCREMENTAL_ENCODER

  #if defined(FEATURE_EL_POSITION_INCREMENTAL_ENCODER) && defined(FEATURE_ELEVATION_CONTROL)
  pinMode(el_incremental_encoder_pin_phase_a, INPUT);
  pinMode(el_incremental_encoder_pin_phase_b, INPUT);
  pinMode(el_incremental_encoder_pin_phase_z, INPUT);
  #ifdef OPTION_INCREMENTAL_ENCODER_PULLUPS
  digitalWrite(el_incremental_encoder_pin_phase_a, HIGH);
  digitalWrite(el_incremental_encoder_pin_phase_b, HIGH);
  digitalWrite(el_incremental_encoder_pin_phase_z, HIGH);
  #endif // OPTION_INCREMENTAL_ENCODER_PULLUPS
  attachInterrupt(EL_POSITION_INCREMENTAL_ENCODER_A_PIN_INTERRUPT, el_position_incremental_encoder_interrupt_handler, CHANGE);
  attachInterrupt(EL_POSITION_INCREMENTAL_ENCODER_B_PIN_INTERRUPT, el_position_incremental_encoder_interrupt_handler, CHANGE);
  delay(250);
  el_3_phase_encoder_last_phase_a_state = digitalRead(el_incremental_encoder_pin_phase_a);
  el_3_phase_encoder_last_phase_b_state = digitalRead(el_incremental_encoder_pin_phase_b);
  #endif // defined(FEATURE_EL_POSITION_INCREMENTAL_ENCODER) && defined(FEATURE_ELEVATION_CONTROL)

} /* initialize_rotary_encoders */


// --------------------------------------------------------------
#ifdef FEATURE_AZ_PRESET_ENCODER
void check_preset_encoders(){

  static unsigned long last_encoder_change_time = 0;
  byte button_read = HIGH;
  byte number_columns = 0;
  static byte submit_encoder_change = 0;
  static unsigned long last_preset_start_button_start = 0;
  static unsigned long last_preset_start_button_kill = 0;
  static unsigned long last_encoder_move = 0;

  #ifdef FEATURE_AZ_PRESET_ENCODER
  static unsigned long az_timestamp[5];
  #endif // FEATURE_AZ_PRESET_ENCODER

  #ifdef FEATURE_EL_PRESET_ENCODER
  static unsigned long el_timestamp[5];
  #endif // FEATURE_EL_PRESET_ENCODER

  #ifdef FEATURE_AZ_PRESET_ENCODER
  az_encoder_state = ttable[az_encoder_state & 0xf][((digitalReadEnhanced(az_rotary_preset_pin2) << 1) | digitalReadEnhanced(az_rotary_preset_pin1))];
  unsigned char az_encoder_result = az_encoder_state & 0x30;
  #ifdef DEBUG_PRESET_ENCODERS
  static byte last_az_rotary_preset_pin1 = 0;
  static byte last_az_rotary_preset_pin2 = 0;

  if ((debug_mode) && (( last_az_rotary_preset_pin1 != digitalReadEnhanced(az_rotary_preset_pin1)) || ( last_az_rotary_preset_pin2 != digitalReadEnhanced(az_rotary_preset_pin2)))) {
    debug_print("check_preset_encoders: az_rotary_preset_pin1: ");
    debug_print_int(digitalReadEnhanced(az_rotary_preset_pin1));
    debug_print(" az_rotary_preset_pin2: ");
    debug_print_int(digitalReadEnhanced(az_rotary_preset_pin2));
    debug_print(" encoder_result: ");
    debug_print_int(az_encoder_result);
    debug_println("");
  }
  last_az_rotary_preset_pin1 = digitalReadEnhanced(az_rotary_preset_pin1);
  last_az_rotary_preset_pin2 = digitalReadEnhanced(az_rotary_preset_pin2);
  #endif // DEBUG_PRESET_ENCODERS

  #endif // FEATURE_AZ_PRESET_ENCODER

  #ifdef FEATURE_EL_PRESET_ENCODER
  el_encoder_state = ttable[el_encoder_state & 0xf][((digitalReadEnhanced(el_rotary_preset_pin2) << 1) | digitalReadEnhanced(el_rotary_preset_pin1))];
  unsigned char el_encoder_result = el_encoder_state & 0x30;
  #endif // FEATURE_EL_PRESET_ENCODER

  #ifdef FEATURE_AZ_PRESET_ENCODER
  if (az_encoder_result) {                                 // If rotary encoder modified
    az_timestamp[0] = az_timestamp[1];                     // Encoder step timer
    az_timestamp[1] = az_timestamp[2];
    az_timestamp[2] = az_timestamp[3];
    az_timestamp[3] = az_timestamp[4];
    az_timestamp[4] = millis();

    last_encoder_move = millis();


    #ifdef DEBUG_PRESET_ENCODERS
    char tempchar[12] = "";
    if (debug_mode) {
      debug_print("check_preset_encoders: az_timestamps:");
      for (int y = 0; y < 5; y++) {
        debug_print(" ");
        dtostrf(az_timestamp[y],0,0,tempchar);
        debug_print(tempchar);
      }
      debug_println("");
    }
    #endif // DEBUG_PRESET_ENCODERS

    unsigned long az_elapsed_time = (az_timestamp[4] - az_timestamp[0]); // Encoder step time difference for 10's step

    #ifdef OPTION_PRESET_ENCODER_RELATIVE_CHANGE
    if ((preset_encoders_state == ENCODER_IDLE) || (preset_encoders_state == ENCODER_EL_PENDING)) {
      if (az_request_queue_state == IN_PROGRESS_TO_TARGET) {
        az_encoder_raw_degrees = target_raw_azimuth;
      } else {
        az_encoder_raw_degrees = raw_azimuth;
      }
    }
    #endif // OPTION_PRESET_ENCODER_RELATIVE_CHANGE

    // bbbbbb

    #ifndef OPTION_PRESET_ENCODER_0_360_DEGREES
    if (az_encoder_result == DIR_CW) {
      #ifdef DEBUG_PRESET_ENCODERS
      debug_print("check_preset_encoders: az CW");
      #endif // DEBUG_PRESET_ENCODERS
      if (az_elapsed_time < 250 /* mSec */) {
        az_encoder_raw_degrees += (5 * HEADING_MULTIPLIER);
      } else { az_encoder_raw_degrees += (1 * HEADING_MULTIPLIER); };                                                                                      // Single deg increase unless encoder turned quickly then 10's step
      // if (az_encoder_raw_degrees >=(360*HEADING_MULTIPLIER)) {az_encoder_raw_degrees -= (360*HEADING_MULTIPLIER);};
      if (az_encoder_raw_degrees > ((azimuth_starting_point + azimuth_rotation_capability) * HEADING_MULTIPLIER)) {
        az_encoder_raw_degrees =
          ((azimuth_starting_point * HEADING_MULTIPLIER)
           /* + ((azimuth_starting_point+azimuth_rotation_capability)*HEADING_MULTIPLIER) - az_encoder_raw_degrees*/);
      }
    }
    if (az_encoder_result == DIR_CCW) {
      #ifdef DEBUG_PRESET_ENCODERS
      debug_print("check_preset_encoders: az CCW");
      #endif // DEBUG_PRESET_ENCODERS
      if (az_elapsed_time < 250 /* mSec */) {
        az_encoder_raw_degrees -= (5 * HEADING_MULTIPLIER);
      } else { az_encoder_raw_degrees -= (1 * HEADING_MULTIPLIER); };                                                                                       // Single deg decrease unless encoder turned quickly then 10's step
      // if (az_encoder_raw_degrees < 0) {az_encoder_raw_degrees = (360*HEADING_MULTIPLIER);};
      if (az_encoder_raw_degrees < (azimuth_starting_point * HEADING_MULTIPLIER)) {
        az_encoder_raw_degrees = (((azimuth_starting_point + azimuth_rotation_capability) * HEADING_MULTIPLIER)
                                  /*- (az_encoder_raw_degrees-(azimuth_starting_point*HEADING_MULTIPLIER))*/);
      }
    }
    #else //ndef OPTION_PRESET_ENCODER_0_360_DEGREES
    if (az_encoder_result == DIR_CW) {
      #ifdef DEBUG_PRESET_ENCODERS
      debug_print("check_preset_encoders: az CW");
      #endif // DEBUG_PRESET_ENCODERS
      if (az_elapsed_time < 250) {  // Single deg increase unless encoder turned quickly then 10's step
        az_encoder_raw_degrees += (5 * HEADING_MULTIPLIER);
      } else {
        az_encoder_raw_degrees += (1 * HEADING_MULTIPLIER);
      }                                                                                   
      if (az_encoder_raw_degrees > (360 * HEADING_MULTIPLIER)) {
        //az_encoder_raw_degrees = (360 * HEADING_MULTIPLIER);
        az_encoder_raw_degrees = 0;
      //} else {
       // if (az_encoder_raw_degrees < 0) {
       //   az_encoder_raw_degrees = 0;
       // }
      }
    }
    if (az_encoder_result == DIR_CCW) {
      #ifdef DEBUG_PRESET_ENCODERS
      debug_print("check_preset_encoders: az CCW");
      #endif // DEBUG_PRESET_ENCODERS
      if (az_elapsed_time < 250) {  // Single deg decrease unless encoder turned quickly then 10's step
        az_encoder_raw_degrees -= (5 * HEADING_MULTIPLIER);
      } else { 
        az_encoder_raw_degrees -= (1 * HEADING_MULTIPLIER); 
      }                                                                                       
      //if (az_encoder_raw_degrees > (360 * HEADING_MULTIPLIER)) {
      //  az_encoder_raw_degrees = (360 * HEADING_MULTIPLIER);
      //} else {
        if (az_encoder_raw_degrees < 0) {
          //az_encoder_raw_degrees = 0;
          az_encoder_raw_degrees = 359 * HEADING_MULTIPLIER;
        }
      //}
    }
    #endif //ndef OPTION_PRESET_ENCODER_0_360_DEGREES



    last_encoder_change_time = millis();     // Encoder Check Timer

    #ifdef FEATURE_LCD_DISPLAY
    push_lcd_update = 1;                     // push an LCD update
    #endif // FEATURE_LCD_DISPLAY

    if (preset_encoders_state == ENCODER_IDLE) {
      preset_encoders_state = ENCODER_AZ_PENDING;
    } else {
      if (preset_encoders_state == ENCODER_EL_PENDING) {
        preset_encoders_state = ENCODER_AZ_EL_PENDING;
      }
    }

    #ifdef DEBUG_PRESET_ENCODERS
    debug_print("check_preset_encoders: az target: ");
    dtostrf((az_encoder_raw_degrees / HEADING_MULTIPLIER),0,1,tempchar);
    debug_println(tempchar);
    #endif // DEBUG_PRESET_ENCODERS

  } // if (az_encoder_result)
  #endif // FEATURE_AZ_PRESET_ENCODER

  #ifdef FEATURE_EL_PRESET_ENCODER

  #ifdef OPTION_PRESET_ENCODER_RELATIVE_CHANGE
  if ((preset_encoders_state == ENCODER_IDLE) || (preset_encoders_state == ENCODER_AZ_PENDING)) {
    if (el_request_queue_state == IN_PROGRESS_TO_TARGET) {
      el_encoder_degrees = target_elevation;
    } else {
      el_encoder_degrees = elevation;
    }
  }
    #endif // OPTION_PRESET_ENCODER_RELATIVE_CHANGE

  if (el_encoder_result) {                                 // If rotary encoder modified
    el_timestamp[0] = el_timestamp[1];                     // Encoder step timer
    el_timestamp[1] = el_timestamp[2];
    el_timestamp[2] = el_timestamp[3];
    el_timestamp[3] = el_timestamp[4];
    el_timestamp[4] = millis();

    last_encoder_move = millis();

    unsigned long el_elapsed_time = (el_timestamp[4] - el_timestamp[0]); // Encoder step time difference for 10's step

    if (el_encoder_result == DIR_CW) {                      // Rotary Encoder CW 0 - 359 Deg
      #ifdef DEBUG_PRESET_ENCODERS
      debug_println("check_preset_encoders: el CW");
      #endif // DEBUG_PRESET_ENCODERS
      if (el_elapsed_time < 250) {
        el_encoder_degrees += (5 * HEADING_MULTIPLIER);
      } else { el_encoder_degrees += (1 * HEADING_MULTIPLIER); };                                                                       // Single deg increase unless encoder turned quickly then 10's step
      if (el_encoder_degrees > (180 * HEADING_MULTIPLIER)) {
        el_encoder_degrees = (180 * HEADING_MULTIPLIER);
      }
      ;
    }
    if (el_encoder_result == DIR_CCW) {
      #ifdef DEBUG_PRESET_ENCODERS
      debug_println("check_preset_encoders: el CCW");
      #endif // DEBUG_PRESET_ENCODERS
      // Rotary Encoder CCW 359 - 0 Deg
      if (el_elapsed_time < 250) {
        el_encoder_degrees -= (5 * HEADING_MULTIPLIER);
      } else { el_encoder_degrees -= (1 * HEADING_MULTIPLIER); };                                                                        // Single deg decrease unless encoder turned quickly then 10's step
      if (el_encoder_degrees < 0) {
        el_encoder_degrees = 0;
      }
      ;
    }
    last_encoder_change_time = millis();     // Encoder Check Timer

    #ifdef FEATURE_LCD_DISPLAY
    last_lcd_update = 0;                     // push an LCD update
    #endif // FEATURE_LCD_DISPLAY

    if (preset_encoders_state == ENCODER_IDLE) {
      preset_encoders_state = ENCODER_EL_PENDING;
    } else {
      if (preset_encoders_state == ENCODER_AZ_PENDING) {
        preset_encoders_state = ENCODER_AZ_EL_PENDING;
      }
    }

    #ifdef DEBUG_PRESET_ENCODERS
    debug_print("check_preset_encoders: el target: ");
    dtostrf(el_encoder_degrees / HEADING_MULTIPLIER,0,1,tempchar);
    debug_println(tempchar);
    #endif // DEBUG_PRESET_ENCODERS


  } // if (el_encoder_result)


  #endif // FEATURE_EL_PRESET_ENCODER

  if ((preset_encoders_state != ENCODER_IDLE) && (!submit_encoder_change)) { // Check button or timer
    if (preset_start_button) {                                               // if we have a preset start button, check it
      button_read = digitalReadEnhanced(preset_start_button);
      if (button_read == BUTTON_ACTIVE_STATE) {
        submit_encoder_change = 1;
        last_preset_start_button_start = millis();

        #ifdef DEBUG_PRESET_ENCODERS
        debug_println("check_preset_encoders: preset_start_button submit_encoder_change");
        #endif // DEBUG_PRESET_ENCODERS
      }
    } else {
      if ((millis() - last_encoder_change_time) > 2000) {       // if enc not changed for more than 2 sec, rotate to target
        #ifdef DEBUG_PRESET_ENCODERS
        debug_println("check_preset_encoders: timer submit_encoder_change");
        #endif // DEBUG_PRESET_ENCODERS
        submit_encoder_change = 1;
      }
    }
  } // if (!enc_changed_waiting)


  if (preset_start_button) {                                         // if we have a preset start button, check it
    button_read = digitalReadEnhanced(preset_start_button);
    if ((button_read == BUTTON_ACTIVE_STATE) && (!submit_encoder_change) && ((millis() - last_preset_start_button_start) > 250)
        && ((millis() - last_preset_start_button_kill) > 250) && (preset_encoders_state == ENCODER_IDLE)) {
      #ifdef DEBUG_PRESET_ENCODERS
      debug_println("check_preset_encoders: preset button kill");
      #endif // DEBUG_PRESET_ENCODERS
      #ifdef FEATURE_AZ_PRESET_ENCODER
      if (az_state != IDLE) {
        submit_request(AZ, REQUEST_KILL, 0, 45);
      }
      #endif // FEATURE_AZ_PRESET_ENCODER
      #ifdef FEATURE_EL_PRESET_ENCODER
      if (el_state != IDLE) {
        submit_request(EL, REQUEST_KILL, 0, 46);
      }
      #endif // FEATURE_EL_PRESET_ENCODER
      last_preset_start_button_kill = millis();
    }
  }

  if ((submit_encoder_change) && (button_read == BUTTON_INACTIVE_STATE)) {
    #ifdef DEBUG_PRESET_ENCODERS
    debug_println("check_preset_encoders: submit_encoder_change ");
    #endif // DEBUG_PRESET_ENCODERS


    if ((preset_encoders_state == ENCODER_AZ_PENDING) || (preset_encoders_state == ENCODER_AZ_EL_PENDING)) {
      #ifndef OPTION_PRESET_ENCODER_0_360_DEGREES
      submit_request(AZ, REQUEST_AZIMUTH_RAW, az_encoder_raw_degrees, 47);
      #else
      submit_request(AZ, REQUEST_AZIMUTH, az_encoder_raw_degrees, 47);
      #endif //ndef OPTION_PRESET_ENCODER_0_360_DEGREES
    }

    #ifdef FEATURE_EL_PRESET_ENCODER
    if ((preset_encoders_state == ENCODER_EL_PENDING) || (preset_encoders_state == ENCODER_AZ_EL_PENDING)) {
      submit_request(EL, REQUEST_ELEVATION, el_encoder_degrees, 48);
    }
    #endif // FEATURE_EL_PRESET_ENCODER

    preset_encoders_state = ENCODER_IDLE;
    submit_encoder_change = 0;
  } // if (submit_encoder_change)

  if ((preset_start_button) && (preset_encoders_state != ENCODER_IDLE) && ((millis() - last_encoder_move) > ENCODER_PRESET_TIMEOUT)) { // timeout if we have a preset start button
    preset_encoders_state = ENCODER_IDLE;
    #ifdef FEATURE_LCD_DISPLAY
    push_lcd_update = 1;                     // push an LCD update
    #endif // FEATURE_LCD_DISPLAY
  }

} /* check_preset_encoders */

#endif // FEATURE_AZ_PRESET_ENCODER

// --------------------------------------------------------------

#ifdef OPTION_AZ_MANUAL_ROTATE_LIMITS
void check_az_manual_rotate_limit() {

  if ((current_az_state() == ROTATING_CCW) && (raw_azimuth <= (AZ_MANUAL_ROTATE_CCW_LIMIT * HEADING_MULTIPLIER))) {
    #ifdef DEBUG_AZ_MANUAL_ROTATE_LIMITS
    debug_print("check_az_manual_rotate_limit: stopping - hit AZ_MANUAL_ROTATE_CCW_LIMIT of ");
    debug_print_int(AZ_MANUAL_ROTATE_CCW_LIMIT);
    debug_println("");
    #endif // DEBUG_AZ_MANUAL_ROTATE_LIMITS
    submit_request(AZ, REQUEST_KILL, 0, 49);
  }
  if ((current_az_state() == ROTATING_CW) && (raw_azimuth >= (AZ_MANUAL_ROTATE_CW_LIMIT * HEADING_MULTIPLIER))) {
    #ifdef DEBUG_AZ_MANUAL_ROTATE_LIMITS
    debug_print("check_az_manual_rotate_limit: stopping - hit AZ_MANUAL_ROTATE_CW_LIMIT of ");
    debug_print_int(AZ_MANUAL_ROTATE_CW_LIMIT);
    debug_println("");
    #endif // DEBUG_AZ_MANUAL_ROTATE_LIMITS
    submit_request(AZ, REQUEST_KILL, 0, 50);
  }
} /* check_az_manual_rotate_limit */
#endif // #ifdef OPTION_AZ_MANUAL_ROTATE_LIMITS

// --------------------------------------------------------------

#if defined(OPTION_EL_MANUAL_ROTATE_LIMITS) && defined(FEATURE_ELEVATION_CONTROL)
void check_el_manual_rotate_limit() {

  if ((current_el_state() == ROTATING_DOWN) && (elevation <= (EL_MANUAL_ROTATE_DOWN_LIMIT * HEADING_MULTIPLIER))) {
    #ifdef DEBUG_EL_MANUAL_ROTATE_LIMITS
    debug_print("check_el_manual_rotate_limit: stopping - hit EL_MANUAL_ROTATE_DOWN_LIMIT of ");
    debug_print_int(EL_MANUAL_ROTATE_DOWN_LIMIT);
    debug_println("");
    #endif // DEBUG_EL_MANUAL_ROTATE_LIMITS
    submit_request(EL, REQUEST_KILL, 0, 51);
  }
  if ((current_el_state() == ROTATING_UP) && (elevation >= (EL_MANUAL_ROTATE_UP_LIMIT * HEADING_MULTIPLIER))) {
    #ifdef DEBUG_EL_MANUAL_ROTATE_LIMITS
    debug_print("check_el_manual_rotate_limit: stopping - hit EL_MANUAL_ROTATE_UP_LIMIT of ");
    debug_print_int(EL_MANUAL_ROTATE_UP_LIMIT);
    debug_println("");
    #endif // DEBUG_EL_MANUAL_ROTATE_LIMITS
    submit_request(EL, REQUEST_KILL, 0, 52);
  }
} /* check_el_manual_rotate_limit */
#endif // #ifdef OPTION_EL_MANUAL_ROTATE_LIMITS


// --------------------------------------------------------------
void check_brake_release() {


  static byte in_az_brake_release_delay = 0;
  static unsigned long az_brake_delay_start_time = 0;

  #ifdef FEATURE_ELEVATION_CONTROL
  static byte in_el_brake_release_delay = 0;
  static unsigned long el_brake_delay_start_time = 0;
  #endif // FEATURE_ELEVATION_CONTROL

  if ((az_state == IDLE) && (brake_az_engaged)) {
    if (in_az_brake_release_delay) {
      if ((millis() - az_brake_delay_start_time) > AZ_BRAKE_DELAY) {
        brake_release(AZ, BRAKE_RELEASE_OFF);
        in_az_brake_release_delay = 0;
      }
    } else {
      az_brake_delay_start_time = millis();
      in_az_brake_release_delay = 1;
    }
  }

  if ((az_state != IDLE) && (brake_az_engaged)) {in_az_brake_release_delay = 0;}

  #ifdef FEATURE_ELEVATION_CONTROL
  if ((el_state == IDLE) && (brake_el_engaged)) {
    if (in_el_brake_release_delay) {
      if ((millis() - el_brake_delay_start_time) > EL_BRAKE_DELAY) {
        brake_release(EL, BRAKE_RELEASE_OFF);
        in_el_brake_release_delay = 0;
      }
    } else {
      el_brake_delay_start_time = millis();
      in_el_brake_release_delay = 1;
    }
  }

  if ((el_state != IDLE) && (brake_el_engaged)) {in_el_brake_release_delay = 0;}  
  #endif // FEATURE_ELEVATION_CONTROL

} /* check_brake_release */

// --------------------------------------------------------------
void brake_release(byte az_or_el, byte operation){

  if (az_or_el == AZ) {
    if (brake_az) {
      if (operation == BRAKE_RELEASE_ON) {
        digitalWriteEnhanced(brake_az, BRAKE_ACTIVE_STATE);
        brake_az_engaged = 1;
        #ifdef DEBUG_BRAKE
        debug_println("brake_release: brake_az BRAKE_RELEASE_ON");
        #endif // DEBUG_BRAKE
      } else {
        digitalWriteEnhanced(brake_az, BRAKE_INACTIVE_STATE);
        brake_az_engaged = 0;
        #ifdef DEBUG_BRAKE
        debug_println("brake_release: brake_az BRAKE_RELEASE_OFF");
        #endif // DEBUG_BRAKE
      }
    }
  } else {
    #ifdef FEATURE_ELEVATION_CONTROL
    if (operation == BRAKE_RELEASE_ON) { 
      if (brake_el) {
        digitalWriteEnhanced(brake_el, BRAKE_ACTIVE_STATE);
        brake_el_engaged = 1;
        #ifdef DEBUG_BRAKE
        debug_println("brake_release: brake_el BRAKE_RELEASE_ON");
        #endif // DEBUG_BRAKE
      } else {
        digitalWriteEnhanced(brake_el, BRAKE_INACTIVE_STATE);
        brake_el_engaged = 0;
        #ifdef DEBUG_BRAKE
        debug_println("brake_release: brake_el BRAKE_RELEASE_OFF");
        #endif // DEBUG_BRAKE
      }
    }
    #endif // FEATURE_ELEVATION_CONTROL
  }
} /* brake_release */

// --------------------------------------------------------------
void check_overlap(){

  static byte overlap_led_status = 0;
  static unsigned long last_check_time;
  #ifdef OPTION_BLINK_OVERLAP_LED
  static unsigned long last_overlap_led_transition = 0;
  static byte blink_status = 0;
  #endif //OPTION_BLINK_OVERLAP_LED

  if ((overlap_led) && ((millis() - last_check_time) > 500)) {
    // if ((analog_az > (500*HEADING_MULTIPLIER)) && (azimuth > (ANALOG_AZ_OVERLAP_DEGREES*HEADING_MULTIPLIER)) && (!overlap_led_status)) {
    if ((raw_azimuth > (ANALOG_AZ_OVERLAP_DEGREES * HEADING_MULTIPLIER)) && (!overlap_led_status)) {
      digitalWriteEnhanced(overlap_led, HIGH);
      overlap_led_status = 1;
      #ifdef OPTION_BLINK_OVERLAP_LED
      last_overlap_led_transition = millis();
      blink_status = 1;
      #endif //OPTION_BLINK_OVERLAP_LED
      #ifdef DEBUG_OVERLAP
      debug_println("check_overlap: in overlap");
      #endif // DEBUG_OVERLAP
    } else {
      // if (((analog_az < (500*HEADING_MULTIPLIER)) || (azimuth < (ANALOG_AZ_OVERLAP_DEGREES*HEADING_MULTIPLIER))) && (overlap_led_status)) {
      if ((raw_azimuth < (ANALOG_AZ_OVERLAP_DEGREES * HEADING_MULTIPLIER)) && (overlap_led_status)) {
        digitalWriteEnhanced(overlap_led, LOW);
        overlap_led_status = 0;
        #ifdef DEBUG_OVERLAP
        debug_println("check_overlap: overlap off");
        #endif // DEBUG_OVERLAP
      }
    }
    last_check_time = millis();

  }

  #ifdef OPTION_BLINK_OVERLAP_LED
  if ((overlap_led_status) && ((millis() - last_overlap_led_transition) >= OPTION_OVERLAP_LED_BLINK_MS)){
    if (blink_status){
      digitalWriteEnhanced(overlap_led, LOW);
      blink_status = 0;
    } else {
      digitalWriteEnhanced(overlap_led, HIGH);
      blink_status = 1;
    }
    last_overlap_led_transition = millis();
  }
  #endif //OPTION_BLINK_OVERLAP_LED

} /* check_overlap */



// --------------------------------------------------------------
void clear_command_buffer(){

  control_port_buffer_index = 0;
  control_port_buffer[0] = 0;


}


// --------------------------------------------------------------
#if defined(FEATURE_REMOTE_UNIT_SLAVE) || defined(FEATURE_ANCILLARY_PIN_CONTROL)
byte get_analog_pin(byte pin_number){

  byte return_output = 0;

  switch (pin_number) {
    case 0: return_output = A0; break;
    case 1: return_output = A1; break;
    case 2: return_output = A2; break;
    case 3: return_output = A3; break;
    case 4: return_output = A4; break;
    case 5: return_output = A5; break;
    case 6: return_output = A6; break;
  }

  return return_output;

}
#endif // FEATURE_REMOTE_UNIT_SLAVE

// --------------------------------------------------------------
#ifdef FEATURE_REMOTE_UNIT_SLAVE
void service_remote_unit_serial_buffer(){

// Goody 2015-03-09 - this may be dead code - all done in check_serial() and proces_remote_slave_command?

/*
 *
 * This implements a protocol for host unit to remote unit communications
 *
 *
 * Remote Slave Unit Protocol Reference
 *
 *  PG - ping
 *  AZ - read azimuth
 *  EL - read elevation
 *  DOxx - digital pin initialize as output;
 *  DIxx - digital pin initialize as input
 *  DPxx - digital pin initialize as input with pullup
 *  DRxx - digital pin read
 *  DLxx - digital pin write low
 *  DHxx - digital pin write high
 *  DTxxyyyy - digital pin tone output
 *  NTxx - no tone
 *  ARxx - analog pin read
 *  AWxxyyy - analog pin write
 *  SWxy - serial write byte
 *  SDx - deactivate serial read event; x = port #
 *  SSxyyyyyy... - serial write sting; x = port #, yyyy = string of characters to send
 *  SAx - activate serial read event; x = port #
 *  RB - reboot
 *
 * Responses
 *
 *  ER - report an error (remote to host only)
 *  EV - report an event (remote to host only)
 *  OK - report success (remote to host only)
 *  CS - report a cold start (remote to host only)
 *
 * Error Codes
 *
 *  ER01 - Serial port buffer timeout
 *  ER02 - Command syntax error
 *
 * Events
 *
 *  EVSxy - Serial port read event; x = serial port number, y = byte returned
 *
 *
 */


  static String command_string; // changed to static 2013-03-27
  byte command_good = 0;

  if (control_port_buffer_carriage_return_flag) {

    if (control_port_buffer_index < 3) {
      control_port->println(F("ER02"));  // we don't have enough characters - syntax error
    } else {
      command_string = String(char(toupper(control_port_buffer[0]))) + String(char(toupper(control_port_buffer[1])));

      #ifdef DEBUG_SERVICE_SERIAL_BUFFER
      debug_print("serial_serial_buffer: command_string: ");
      debug_print(command_string);
      debug_print("$ control_port_buffer_index: ");
      debug_print_int(control_port_buffer_index);
      debug_println("");
      #endif // DEBUG_SERVICE_SERIAL_BUFFER

      if ((command_string == "SS") && (control_port_buffer[2] > 47) && (control_port_buffer[2] < 53)) { // this is a variable length command
        command_good = 1;
        for (byte x = 3; x < control_port_buffer_index; x++) {
          switch (control_port_buffer[2] - 48) {
            case 0: control_port->write(control_port_buffer[x]); break;
            #if defined(FEATURE_MASTER_WITH_SERIAL_SLAVE)
            case 1: remote_unit_port->write(control_port_buffer[x]); break;
            #endif
          }
        }
      }

      if (control_port_buffer_index == 3) {

        if (command_string == "PG") {
          control_port->println(F("PG")); command_good = 1;
        }                                                                        // PG - ping
        if (command_string == "RB") {
          wdt_enable(WDTO_30MS); while (1) {
          }
        }                                                                        // RB - reboot
        if (command_string == "AZ") {
          control_port->print(F("AZ"));
          if (raw_azimuth < 1000) {
            control_port->print("0");
          }
          if (raw_azimuth < 100) {
            control_port->print("0");
          }
          if (raw_azimuth < 10) {
            control_port->print("0");
          }
          control_port->println(raw_azimuth);
          command_good = 1;
        }
        #ifdef FEATURE_ELEVATION_CONTROL
        if (command_string == "EL") {
          control_port->print(F("EL"));
          if (elevation >= 0) {
            control_port->print("+");
          } else {
            control_port->print("-");
          }
          if (abs(elevation) < 1000) {
            control_port->print("0");
          }
          if (abs(elevation) < 100) {
            control_port->print("0");
          }
          if (abs(elevation) < 10) {
            control_port->print("0");
          }
          control_port->println(abs(elevation));
          command_good = 1;
        }
          #endif // FEATURE_ELEVATION_CONTROL
      } // end of three byte commands



      if (control_port_buffer_index == 4) {
        if ((command_string == "SA") & (control_port_buffer[2] > 47) && (control_port_buffer[2] < 53)) {
          serial_read_event_flag[control_port_buffer[2] - 48] = 1;
          command_good = 1;
          control_port->println("OK");
        }
        if ((command_string == "SD") & (control_port_buffer[2] > 47) && (control_port_buffer[2] < 53)) {
          serial_read_event_flag[control_port_buffer[2] - 48] = 0;
          command_good = 1;
          control_port->println("OK");
        }

      }


      if (control_port_buffer_index == 5) {
        if (command_string == "SW") { // Serial Write command
          switch (control_port_buffer[2]) {
            case '0': control_port->write(control_port_buffer[3]); command_good = 1; break;
            #if defined(FEATURE_MASTER_WITH_SERIAL_SLAVE)
            case '1': remote_unit_port->write(control_port_buffer[3]); command_good = 1; break;
            #endif
          }
        }

        if (command_string == "DO") {
          if ((((control_port_buffer[2] > 47) && (control_port_buffer[2] < 58)) || (toupper(control_port_buffer[2]) == 'A')) && (control_port_buffer[3] > 47) && (control_port_buffer[3] < 58)) {
            command_good = 1;
            byte pin_value = 0;
            if (toupper(control_port_buffer[2]) == 'A') {
              pin_value = get_analog_pin(control_port_buffer[3] - 48);
            } else {
              pin_value = ((control_port_buffer[2] - 48) * 10) + (control_port_buffer[3] - 48);
            }
            #ifdef DEBUG_SERVICE_SERIAL_BUFFER
            debug_print("service_serial_buffer: pin_value: ");
            debug_print_int(pin_value);
            debug_println("");
            #endif // DEBUG_SERVICE_SERIAL_BUFFER
            control_port->println("OK");
            pinModeEnhanced(pin_value, OUTPUT);
          }
        }

        if (command_string == "DH") {
          if ((((control_port_buffer[2] > 47) && (control_port_buffer[2] < 58)) || (toupper(control_port_buffer[2]) == 'A')) && (control_port_buffer[3] > 47) && (control_port_buffer[3] < 58)) {
            command_good = 1;
            byte pin_value = 0;
            if (toupper(control_port_buffer[2]) == 'A') {
              pin_value = get_analog_pin(control_port_buffer[3] - 48);
            } else {
              pin_value = ((control_port_buffer[2] - 48) * 10) + (control_port_buffer[3] - 48);
            }
            digitalWriteEnhanced(pin_value, HIGH);
            control_port->println("OK");
          }
        }

        if (command_string == "DL") {
          if ((((control_port_buffer[2] > 47) && (control_port_buffer[2] < 58)) || (toupper(control_port_buffer[2]) == 'A')) && (control_port_buffer[3] > 47) && (control_port_buffer[3] < 58)) {
            command_good = 1;
            byte pin_value = 0;
            if (toupper(control_port_buffer[2]) == 'A') {
              pin_value = get_analog_pin(control_port_buffer[3] - 48);
            } else {
              pin_value = ((control_port_buffer[2] - 48) * 10) + (control_port_buffer[3] - 48);
            }
            digitalWriteEnhanced(pin_value, LOW);
            control_port->println("OK");
          }
        }

        if (command_string == "DI") {
          if ((((control_port_buffer[2] > 47) && (control_port_buffer[2] < 58)) || (toupper(control_port_buffer[2]) == 'A')) && (control_port_buffer[3] > 47) && (control_port_buffer[3] < 58)) {
            command_good = 1;
            byte pin_value = 0;
            if (toupper(control_port_buffer[2]) == 'A') {
              pin_value = get_analog_pin(control_port_buffer[3] - 48);
            } else {
              pin_value = ((control_port_buffer[2] - 48) * 10) + (control_port_buffer[3] - 48);
            }
            pinModeEnhanced(pin_value, INPUT);
            control_port->println("OK");
          }
        }

        if (command_string == "DP") {
          if ((((control_port_buffer[2] > 47) && (control_port_buffer[2] < 58)) || (toupper(control_port_buffer[2]) == 'A')) && (control_port_buffer[3] > 47) && (control_port_buffer[3] < 58)) {
            command_good = 1;
            byte pin_value = 0;
            if (toupper(control_port_buffer[2]) == 'A') {
              pin_value = get_analog_pin(control_port_buffer[3] - 48);
            } else {
              pin_value = ((control_port_buffer[2] - 48) * 10) + (control_port_buffer[3] - 48);
            }
            // pinModeEnhanced(pin_value,INPUT_PULLUP);
            pinModeEnhanced(pin_value, INPUT);
            digitalWriteEnhanced(pin_value, HIGH);
            control_port->println("OK");
          }
        }

        if (command_string == "DR") {
          if ((((control_port_buffer[2] > 47) && (control_port_buffer[2] < 58)) || (toupper(control_port_buffer[2]) == 'A')) && (control_port_buffer[3] > 47) && (control_port_buffer[3] < 58)) {
            command_good = 1;
            byte pin_value = 0;
            if (toupper(control_port_buffer[2]) == 'A') {
              pin_value = get_analog_pin(control_port_buffer[3] - 48);
            } else {
              pin_value = ((control_port_buffer[2] - 48) * 10) + (control_port_buffer[3] - 48);
            }
            byte pin_read = digitalReadEnhanced(pin_value);
            control_port->print("DR");
            control_port->write(control_port_buffer[2]);
            control_port->write(control_port_buffer[3]);
            if (pin_read) {
              control_port->println("1");
            } else {
              control_port->println("0");
            }
          }
        }
        if (command_string == "AR") {
          if ((((control_port_buffer[2] > 47) && (control_port_buffer[2] < 58)) || (toupper(control_port_buffer[2]) == 'A')) && (control_port_buffer[3] > 47) && (control_port_buffer[3] < 58)) {
            command_good = 1;
            byte pin_value = 0;
            if (toupper(control_port_buffer[2]) == 'A') {
              pin_value = get_analog_pin(control_port_buffer[3] - 48);
            } else {
              pin_value = ((control_port_buffer[2] - 48) * 10) + (control_port_buffer[3] - 48);
            }
            int pin_read = analogReadEnhanced(pin_value);
            control_port->print("AR");
            control_port->write(control_port_buffer[2]);
            control_port->write(control_port_buffer[3]);
            if (pin_read < 1000) {
              control_port->print("0");
            }
            if (pin_read < 100) {
              control_port->print("0");
            }
            if (pin_read < 10) {
              control_port->print("0");
            }
            control_port->println(pin_read);
          }
        }

        if (command_string == "NT") {
          if ((((control_port_buffer[2] > 47) && (control_port_buffer[2] < 58)) || (toupper(control_port_buffer[2]) == 'A')) && (control_port_buffer[3] > 47) && (control_port_buffer[3] < 58)) {
            command_good = 1;
            byte pin_value = 0;
            if (toupper(control_port_buffer[2]) == 'A') {
              pin_value = get_analog_pin(control_port_buffer[3] - 48);
            } else {
              pin_value = ((control_port_buffer[2] - 48) * 10) + (control_port_buffer[3] - 48);
            }
            noTone(pin_value);
            control_port->println("OK");
          }
        }

      } // if (control_port_buffer_index == 5)

      if (control_port_buffer_index == 8) {
        if (command_string == "AW") {
          if ((((control_port_buffer[2] > 47) && (control_port_buffer[2] < 58)) || (toupper(control_port_buffer[2]) == 'A')) && (control_port_buffer[3] > 47) && (control_port_buffer[3] < 58)) {
            byte pin_value = 0;
            if (toupper(control_port_buffer[2]) == 'A') {
              pin_value = get_analog_pin(control_port_buffer[3] - 48);
            } else {
              pin_value = ((control_port_buffer[2] - 48) * 10) + (control_port_buffer[3] - 48);
            }
            int write_value = ((control_port_buffer[4] - 48) * 100) + ((control_port_buffer[5] - 48) * 10) + (control_port_buffer[6] - 48);
            if ((write_value >= 0) && (write_value < 256)) {
              analogWriteEnhanced(pin_value, write_value);
              control_port->println("OK");
              command_good = 1;
            }
          }
        }
      }

      if (control_port_buffer_index == 9) {
        if (command_string == "DT") {
          if ((((control_port_buffer[2] > 47) && (control_port_buffer[2] < 58)) || (toupper(control_port_buffer[2]) == 'A')) && (control_port_buffer[3] > 47) && (control_port_buffer[3] < 58)) {
            byte pin_value = 0;
            if (toupper(control_port_buffer[2]) == 'A') {
              pin_value = get_analog_pin(control_port_buffer[3] - 48);
            } else {
              pin_value = ((control_port_buffer[2] - 48) * 10) + (control_port_buffer[3] - 48);
            }
            int write_value = ((control_port_buffer[4] - 48) * 1000) + ((control_port_buffer[5] - 48) * 100) + ((control_port_buffer[6] - 48) * 10) + (control_port_buffer[7] - 48);
            if ((write_value >= 0) && (write_value <= 9999)) {
              tone(pin_value, write_value);
              control_port->println("OK");
              command_good = 1;
            }
          }
        }
      }


      if (!command_good) {
        control_port->println(F("ER02"));
      }
    }
    control_port_buffer_carriage_return_flag = 0;
    control_port_buffer_index = 0;
  } else {
    if (((millis() - last_serial_receive_time) > REMOTE_BUFFER_TIMEOUT_MS) && control_port_buffer_index) {
      control_port->println(F("ER01"));
      control_port_buffer_index = 0;
    }
  }



} /* service_remote_unit_serial_buffer */

      #endif // FEATURE_REMOTE_UNIT_SLAVE
// --------------------------------------------------------------
void check_serial(){
//added  Craig Benson UNSW@ADFA 2015-05-01 to debug
//    debug_println("check_serial()\n ");
//Serial.begin(9600);
//Serial.println("starting check_serial \n");
//delay(100);
//Serial.print(control_port->available());
//Serial.println(".\n");
//end added 2015-05-01

  static unsigned long serial_led_time = 0;
  float tempfloat = 0;
  char return_string[100] = ""; 

  #if !defined(FEATURE_AZ_POSITION_ROTARY_ENCODER) && !defined(FEATURE_AZ_POSITION_PULSE_INPUT)
  long place_multiplier = 0;
  byte decimalplace = 0;
  #endif

  #ifdef FEATURE_CLOCK
  int temp_year = 0;
  byte temp_month = 0;
  byte temp_day = 0;
  byte temp_minute = 0;
  byte temp_hour = 0;
  #endif // FEATURE_CLOCK

  #if defined(FEATURE_MOON_TRACKING) || defined(FEATURE_SUN_TRACKING)
  char grid[10] = "";
  byte hit_error = 0;
  #endif // defined(FEATURE_MOON_TRACKING) || defined(FEATURE_SUN_TRACKING)

  #if defined(FEATURE_AZ_POSITION_ROTARY_ENCODER) || defined(FEATURE_AZ_POSITION_PULSE_INPUT)
  int new_azimuth = 9999;
  #endif
  #ifdef FEATURE_ELEVATION_CONTROL
  #if defined(FEATURE_EL_POSITION_ROTARY_ENCODER) || defined(FEATURE_EL_POSITION_PULSE_INPUT)
  int new_elevation = 9999;
  #endif // FEATURE_ELEVATION_CONTROL
  #endif // defined(FEATURE_AZ_POSITION_ROTARY_ENCODER) || defined(FEATURE_AZ_POSITION_PULSE_INPUT)


  #if defined(FEATURE_REMOTE_UNIT_SLAVE) || defined(FEATURE_YAESU_EMULATION) || defined(FEATURE_EASYCOM_EMULATION)

  if ((serial_led) && (serial_led_time != 0) && ((millis() - serial_led_time) > SERIAL_LED_TIME_MS)) {
    digitalWriteEnhanced(serial_led, LOW);
    serial_led_time = 0;
  }
//added  Craig Benson UNSW@ADFA 2015-05-01 to debug
//Serial.begin(9600);
//Serial.println("Controlport available =\n ");
//delay(1000);
//Serial.print(control_port->available());
//Serial.println(".\n");
//end added 2015-05-01

  if (control_port->available()) {
    if (serial_led) {
      digitalWriteEnhanced(serial_led, HIGH);                      // blink the LED just to say we got something
      serial_led_time = millis();
    }

    #ifdef FEATURE_POWER_SWITCH
    last_activity_time = millis();
    #endif //FEATURE_POWER_SWITCH

    #ifdef DEBUG_SERIAL
    int control_port_available = control_port->available();
    #endif // DEBUG_SERIAL
//added Craig Benson UNSW@ADFA 2015-05-07 (copied from above/below)
//    int control_port_available = control_port->available();
//    debug_print("check_serial: control_port: ");
//    debug_print_int(control_port_available);
//    debug_print(":");    
//end added 2015-05-07

    incoming_serial_byte = control_port->read();
    last_serial_receive_time = millis();

    #ifdef DEBUG_SERIAL
    debug_print("check_serial: control_port: ");
    debug_print_int(control_port_available);
    debug_print(":");
    debug_print_int(incoming_serial_byte);
    debug_println("");
    #endif // DEBUG_SERIAL
//added Craig Benson UNSW@ADFA 2015-05-07 (copied from above/below)
//    debug_print_int(incoming_serial_byte);
    //debug_print(incoming_serial_byte,DEC);
//    debug_println("\n");
//end added 2015-05-07

    if ((incoming_serial_byte > 96) && (incoming_serial_byte < 123)) {  // uppercase it
      incoming_serial_byte = incoming_serial_byte - 32;
    }                                                                                                                    


    #ifdef FEATURE_EASYCOM_EMULATION   //Easycom uses spaces, linefeeds, and carriage returns as command delimiters----------
//added Craig Benson UNSW@ADFA 2015-05-07
//debug_print("Easycomm");
//end added 2015-05-07
    // Easycom only

    if ((control_port_buffer[0] == '\\') or (control_port_buffer[0] == '/') or ((control_port_buffer_index == 0) && ((incoming_serial_byte == '\\') || (incoming_serial_byte == '/')))) {
      // if it's a backslash command add it to the buffer if it's not a line feed or carriage return
      if ((incoming_serial_byte != 10) && (incoming_serial_byte != 13)) { 
        control_port_buffer[control_port_buffer_index] = incoming_serial_byte;
        control_port_buffer_index++;
      }
    } else {
      // if it's an easycom command add it to the buffer if it's not a line feed, carriage return, or space
      if ((incoming_serial_byte != 10) && (incoming_serial_byte != 13) && (incoming_serial_byte != 32)) { 
        control_port_buffer[control_port_buffer_index] = incoming_serial_byte;
        control_port_buffer_index++;
      }
    }

    // if it is an Easycom command and we have a space, line feed, or carriage return, process it
    if (((incoming_serial_byte == 10) || (incoming_serial_byte == 13) || (incoming_serial_byte == 32)) && (control_port_buffer[0] != '\\') && (control_port_buffer[0] != '/')){
      #if defined(OPTION_HAMLIB_EASYCOM_AZ_EL_COMMAND_HACK) && defined(FEATURE_ELEVATION_CONTROL)
      if ((control_port_buffer[0]=='A') && (control_port_buffer[1]=='Z') && (control_port_buffer_index == 2)){
        unsigned long start_time_hack = millis();
        if (!control_port->available()){
          while (((millis() - start_time_hack) < 200) && (!control_port->available())){}  // wait 200 mS for something else to pop up on the serial port
        }
        if (control_port->available()){ // is there also 'EL ' waiting for us in the buffer?
          start_time_hack = millis();
          while ( (control_port->available()) && ((millis() - start_time_hack) < 200) ) {
            control_port->read();
          }
          control_port_buffer[0] = 'Z';
          process_easycom_command(control_port_buffer,1,CONTROL_PORT0,return_string);
          //control_port->println(return_string); 
          control_port->print(return_string);
          #ifndef OPTION_HAMLIB_EASYCOM_NO_TERMINATOR_CHARACTER_HACK
          control_port->write(incoming_serial_byte);
          #endif //OPTION_HAMLIB_EASYCOM_NO_TERMINATOR_CHARACTER_HACK       
        } else {  // we got just a bare AZ command
          process_easycom_command(control_port_buffer,control_port_buffer_index,CONTROL_PORT0,return_string);
          //control_port->println(return_string); 
          control_port->print(return_string);
          #ifndef OPTION_HAMLIB_EASYCOM_NO_TERMINATOR_CHARACTER_HACK
          control_port->write(incoming_serial_byte);
          #endif //OPTION_HAMLIB_EASYCOM_NO_TERMINATOR_CHARACTER_HACK 
        }
      } else {

        if (control_port_buffer_index > 1){
          process_easycom_command(control_port_buffer,control_port_buffer_index,CONTROL_PORT0,return_string);
          //control_port->println(return_string);
          control_port->print(return_string);
          #ifndef OPTION_HAMLIB_EASYCOM_NO_TERMINATOR_CHARACTER_HACK
          control_port->write(incoming_serial_byte);
          #endif //OPTION_HAMLIB_EASYCOM_NO_TERMINATOR_CHARACTER_HACK 
        }

      }
      #else //defined(OPTION_HAMLIB_EASYCOM_AZ_EL_COMMAND_HACK) && defined(FEATURE_ELEVATION_CONTROL)
      if (control_port_buffer_index > 1){
        process_easycom_command(control_port_buffer,control_port_buffer_index,CONTROL_PORT0,return_string);
        //control_port->println(return_string); 
        control_port->print(return_string);
        #ifndef OPTION_HAMLIB_EASYCOM_NO_TERMINATOR_CHARACTER_HACK
        control_port->write(incoming_serial_byte);
        #endif //OPTION_HAMLIB_EASYCOM_NO_TERMINATOR_CHARACTER_HACK 
      }
      #endif //defined(OPTION_HAMLIB_EASYCOM_AZ_EL_COMMAND_HACK) && defined(FEATURE_ELEVATION_CONTROL)
      clear_command_buffer();
    } else {
      // if it is a backslash command, process it if we have a carriage return
      if ((incoming_serial_byte == 13) && ((control_port_buffer[0] == '\\') or (control_port_buffer[0] == '/'))){
        process_backslash_command(control_port_buffer, control_port_buffer_index, CONTROL_PORT0, return_string);
        control_port->println(return_string);
        clear_command_buffer();
      }
    }


    #else //FEATURE_EASYCOM_EMULATION ------------------------------------------------------
//added Craig Benson UNSW@ADFA 2015-05-07
//debug_print("Yaesu incoming byte :");
//debug_print_int(incoming_serial_byte);
//debug_println("\n");
//end added 2015-05-07
    // Yaesu, Remote Slave
    if ((incoming_serial_byte != 10) && (incoming_serial_byte != 13)) { // add it to the buffer if it's not a line feed or carriage return
      control_port_buffer[control_port_buffer_index] = incoming_serial_byte;
      control_port_buffer_index++;
    }

    if (incoming_serial_byte == 13) {  // do we have a carriage return?
  //added Craig Benson UNSW@ADFA 2015-05-07
//  debug_print_int(control_port_buffer[2]);
//  debug_println("that was control_port_buffer\n");
//  delay(100);
  //end added 2015-05-07
      if ((control_port_buffer[0] == '\\') or (control_port_buffer[0] == '/')) {
        process_backslash_command(control_port_buffer, control_port_buffer_index, CONTROL_PORT0, return_string);
      } else {
        #ifdef FEATURE_YAESU_EMULATION 
        process_yaesu_command(control_port_buffer,control_port_buffer_index,CONTROL_PORT0,return_string);
        #endif //FEATURE_YAESU_EMULATION

        #ifdef FEATURE_REMOTE_UNIT_SLAVE
        process_remote_slave_command(control_port_buffer,control_port_buffer_index,CONTROL_PORT0,return_string);
        #endif //FEATURE_REMOTE_UNIT_SLAVE
      }  
      control_port->println(return_string);
      clear_command_buffer();
    }

    #endif //FEATURE_EASYCOM_EMULATION--------------------------



  } // if (control_port->available())
  #endif // defined(FEATURE_REMOTE_UNIT_SLAVE) || defined(FEATURE_YAESU_EMULATION) || defined(FEATURE_EASYCOM_EMULATION)


  // remote unit port servicing
  #if defined(FEATURE_MASTER_WITH_SERIAL_SLAVE)
  if (remote_unit_port->available()) {
    incoming_serial_byte = remote_unit_port->read();

    #if defined(FEATURE_REMOTE_UNIT_SLAVE) || defined(FEATURE_YAESU_EMULATION) || defined(FEATURE_EASYCOM_EMULATION)
    // if (serial_read_event_flag[1]) {
    //   control_port->print("EVS1");
    //   control_port->write(incoming_serial_byte);
    //   control_port->println();
    // }
    if (remote_port_rx_sniff) {
      control_port->write(incoming_serial_byte);
    }
    #endif //defined(FEATURE_REMOTE_UNIT_SLAVE) || defined(FEATURE_YAESU_EMULATION) || defined(FEATURE_EASYCOM_EMULATION)

    if ((incoming_serial_byte != 10) && (remote_unit_port_buffer_index < COMMAND_BUFFER_SIZE)) {
      // incoming_serial_byte = toupper(incoming_serial_byte);
      remote_unit_port_buffer[remote_unit_port_buffer_index] = incoming_serial_byte;
      remote_unit_port_buffer_index++;
      if ((incoming_serial_byte == 13) || (remote_unit_port_buffer_index == COMMAND_BUFFER_SIZE)) {
        remote_unit_port_buffer_carriage_return_flag = 1;
      }
    }
    serial1_last_receive_time = millis();

  }
  #endif // defined(FEATURE_MASTER_WITH_SERIAL_SLAVE)


  #ifdef FEATURE_GPS
  if (gps_port->available()) {
    byte gps_port_read = gps_port->read();
    #ifdef GPS_MIRROR_PORT
    gps_mirror_port->write(gps_port_read);
    #endif //GPS_MIRROR_PORT
    #ifdef DEBUG_GPS_SERIAL
    debug_print_char(gps_port_read);
    //port_flush();    
    #endif //DEBUG_GPS_SERIAL
    if (gps.encode(gps_port_read)) {
      gps_data_available = 1;
    }
  }
  #endif // FEATURE_GPS

  #if defined(GPS_MIRROR_PORT) && defined(FEATURE_GPS)
  if (gps_mirror_port->available()) {
    gps_port->write(gps_mirror_port->read());
  }
  #endif //defined(GPS_MIRROR_PORT) && defined(FEATURE_GPS)


} /* check_serial */


// --------------------------------------------------------------
void check_buttons(){

  #ifdef FEATURE_ADAFRUIT_BUTTONS
  int buttons = 0;
  buttons = lcd.readButtons();

  if (buttons & BUTTON_RIGHT) {
  #else
  if (button_cw && (digitalReadEnhanced(button_cw) == BUTTON_ACTIVE_STATE)) {
  #endif // FEATURE_ADAFRUIT_BUTTONS
    if (azimuth_button_was_pushed == 0) {
    #ifdef DEBUG_BUTTONS
    debug_println("check_buttons: button_cw pushed");
    #endif // DEBUG_BUTTONS
    #ifdef OPTION_AZ_MANUAL_ROTATE_LIMITS
    if (raw_azimuth < (AZ_MANUAL_ROTATE_CW_LIMIT * HEADING_MULTIPLIER)) {
      #endif
      submit_request(AZ, REQUEST_CW, 0, 61);
      azimuth_button_was_pushed = 1;
      #ifdef OPTION_AZ_MANUAL_ROTATE_LIMITS
    } else {
      #ifdef DEBUG_BUTTONS
      debug_println("check_buttons: exceeded AZ_MANUAL_ROTATE_CW_LIMIT");
      #endif // DEBUG_BUTTONS
    }
      #endif
    }

  } else {
    #ifdef FEATURE_ADAFRUIT_BUTTONS
    if (buttons & BUTTON_LEFT) {
    #else
    if (button_ccw && (digitalReadEnhanced(button_ccw) == BUTTON_ACTIVE_STATE)) {
    #endif // FEATURE_ADAFRUIT_BUTTONS
    if (azimuth_button_was_pushed == 0) {
    #ifdef DEBUG_BUTTONS
    debug_println("check_buttons: button_ccw pushed");
    #endif // DEBUG_BUTTONS  
    #ifdef OPTION_AZ_MANUAL_ROTATE_LIMITS
        if (raw_azimuth > (AZ_MANUAL_ROTATE_CCW_LIMIT * HEADING_MULTIPLIER)) {
      #endif
        submit_request(AZ, REQUEST_CCW, 0, 62);
        azimuth_button_was_pushed = 1;
      #ifdef OPTION_AZ_MANUAL_ROTATE_LIMITS
      } else {
        #ifdef DEBUG_BUTTONS
        debug_println("check_buttons: exceeded AZ_MANUAL_ROTATE_CCW_LIMIT");
        #endif // DEBUG_BUTTONS
      }
      #endif // OPTION_AZ_MANUAL_ROTATE_LIMITS
      }
    }
  }

#ifdef FEATURE_ADAFRUIT_BUTTONS
  if ((azimuth_button_was_pushed) && (!(buttons & 0x12))) {
    #ifdef DEBUG_BUTTONS
    debug_println("check_buttons: no button depressed");
    #endif // DEBUG_BUTTONS
    submit_request(AZ, REQUEST_STOP, 0, 63);
    azimuth_button_was_pushed = 0;
  }

#else
  if ((azimuth_button_was_pushed) && (digitalReadEnhanced(button_ccw) == BUTTON_INACTIVE_STATE) && (digitalReadEnhanced(button_cw) == BUTTON_INACTIVE_STATE)) {
    delay(200);
    if ((digitalReadEnhanced(button_ccw) == BUTTON_INACTIVE_STATE) && (digitalReadEnhanced(button_cw) == BUTTON_INACTIVE_STATE)) {
    #ifdef DEBUG_BUTTONS
    debug_println("check_buttons: no AZ button depressed");
    #endif // DEBUG_BUTTONS
    #ifndef OPTION_BUTTON_RELEASE_NO_SLOWDOWN
    submit_request(AZ, REQUEST_STOP, 0, 64);
    #else
    submit_request(AZ, REQUEST_KILL, 0, 65);
    #endif // OPTION_BUTTON_RELEASE_NO_SLOWDOWN
    azimuth_button_was_pushed = 0;
    }
  }
#endif // FEATURE_ADAFRUIT_BUTTONS

#ifdef FEATURE_ELEVATION_CONTROL
#ifdef FEATURE_ADAFRUIT_BUTTONS
  if (buttons & 0x08) {
  #else
  if (button_up && (digitalReadEnhanced(button_up) == BUTTON_ACTIVE_STATE)) {
  #endif // FEATURE_ADAFRUIT_BUTTONS
    if (elevation_button_was_pushed == 0) {
      submit_request(EL, REQUEST_UP, 0, 66);
      elevation_button_was_pushed = 1;
      #ifdef DEBUG_BUTTONS
      debug_println("check_buttons: button_up pushed");
      #endif // DEBUG_BUTTONS  
    }

  } else {
    #ifdef FEATURE_ADAFRUIT_BUTTONS
    if (buttons & 0x04) {
    #else
    if (button_down && (digitalReadEnhanced(button_down) == BUTTON_ACTIVE_STATE)) {
    #endif // FEATURE_ADAFRUIT_BUTTONS
      if (elevation_button_was_pushed == 0) {
        submit_request(EL, REQUEST_DOWN, 0, 67);
        elevation_button_was_pushed = 1;
      #ifdef DEBUG_BUTTONS
      debug_println("check_buttons: button_down pushed");
      #endif // DEBUG_BUTTONS    
      }
    }
  }

#ifdef FEATURE_ADAFRUIT_BUTTONS
  if ((elevation_button_was_pushed) && (!(buttons & 0x0C))) {
  #ifdef DEBUG_BUTTONS
  debug_println("check_buttons: no EL button depressed");
  #endif // DEBUG_BUTTONS
  #ifndef OPTION_BUTTON_RELEASE_NO_SLOWDOWN
    submit_request(EL, REQUEST_STOP, 0, 68);
  #else
    submit_request(EL, REQUEST_KILL, 0, 69);
  #endif // OPTION_BUTTON_RELEASE_NO_SLOWDOWN
    elevation_button_was_pushed = 0;
  }

#else
  if ((elevation_button_was_pushed) && (digitalReadEnhanced(button_up) == BUTTON_INACTIVE_STATE) && (digitalReadEnhanced(button_down) == BUTTON_INACTIVE_STATE)) {
    delay(200);
    if ((digitalReadEnhanced(button_up) == BUTTON_INACTIVE_STATE) && (digitalReadEnhanced(button_down) == BUTTON_INACTIVE_STATE)) {
    #ifdef DEBUG_BUTTONS
    debug_println("check_buttons: no EL button depressed");
    #endif // DEBUG_BUTTONS
    #ifndef OPTION_BUTTON_RELEASE_NO_SLOWDOWN
      submit_request(EL, REQUEST_STOP, 0, 70);
    #else
      submit_request(EL, REQUEST_KILL, 0, 71);
    #endif // OPTION_BUTTON_RELEASE_NO_SLOWDOWN
      elevation_button_was_pushed = 0;
    }
  }
#endif // FEATURE_ADAFRUIT_BUTTONS

#endif // FEATURE_ELEVATION_CONTROL


#ifdef FEATURE_PARK
  static byte park_button_pushed = 0;
  static unsigned long last_time_park_button_pushed = 0;

  if (button_park) {
    if ((digitalReadEnhanced(button_park) == BUTTON_ACTIVE_STATE)) {
      park_button_pushed = 1;
      last_time_park_button_pushed = millis();
    #ifdef DEBUG_BUTTONS
    debug_println("check_buttons: button_park pushed");
    #endif // DEBUG_BUTTONS   
    } else {
      if ((park_button_pushed) && ((millis() - last_time_park_button_pushed) >= 250)) {
        if (park_status != PARK_INITIATED) {
          #ifdef DEBUG_BUTTONS
          debug_println("check_buttons: executing park");
          #endif // DEBUG_BUTTONS
          initiate_park();
        } else {
          #ifdef DEBUG_BUTTONS
          debug_println("check_buttons: park aborted");
          #endif // DEBUG_BUTTONS
          submit_request(AZ, REQUEST_KILL, 0, 72);
            #ifdef FEATURE_ELEVATION_CONTROL
          submit_request(EL, REQUEST_KILL, 0, 73);
            #endif // FEATURE_ELEVATION_CONTROL
        }
        park_button_pushed = 0;
      }
    }

  }

    #endif /* ifdef FEATURE_PARK */


  if (button_stop) {
    if ((digitalReadEnhanced(button_stop) == BUTTON_ACTIVE_STATE)) {
      #ifdef DEBUG_BUTTONS
      debug_println("check_buttons: button_stop pushed");
      #endif // DEBUG_BUTTONS      
      #ifndef OPTION_BUTTON_RELEASE_NO_SLOWDOWN
      submit_request(AZ, REQUEST_STOP, 0, 74);
      #else
      submit_request(AZ, REQUEST_KILL, 0, 75);
      #endif // OPTION_BUTTON_RELEASE_NO_SLOWDOWN
      #ifdef FEATURE_ELEVATION_CONTROL
      #ifndef OPTION_BUTTON_RELEASE_NO_SLOWDOWN
      submit_request(EL, REQUEST_STOP, 0, 76);
      #else
      submit_request(EL, REQUEST_KILL, 0, 77);
      #endif // OPTION_BUTTON_RELEASE_NO_SLOWDOWN
      #endif // FEATURE_ELEVATION_CONTROL
    }
  }

  #ifdef FEATURE_MOON_TRACKING
  static byte moon_tracking_button_pushed = 0;
  static unsigned long last_time_moon_tracking_button_pushed = 0;
  if (moon_tracking_button) {
    if ((digitalReadEnhanced(moon_tracking_button) == BUTTON_ACTIVE_STATE)) {
      moon_tracking_button_pushed = 1;
      last_time_moon_tracking_button_pushed = millis();
      #ifdef DEBUG_BUTTONS
      debug_println("check_buttons: moon_tracking_button pushed");
      #endif // DEBUG_BUTTONS
    } else {
      if ((moon_tracking_button_pushed) && ((millis() - last_time_moon_tracking_button_pushed) >= 250)) {
        if (!moon_tracking_active) {
          #ifdef DEBUG_BUTTONS
          debug_println("check_buttons: moon tracking on");
          #endif // DEBUG_BUTTONS
          moon_tracking_active = 1;
          #ifdef FEATURE_SUN_TRACKING
          sun_tracking_active = 0;
          #endif // FEATURE_SUN_TRACKING          
        } else {
          #ifdef DEBUG_BUTTONS
           debug_println("check_buttons: moon tracking off");
          #endif // DEBUG_BUTTONS
          moon_tracking_active = 0;
        }
        moon_tracking_button_pushed = 0;
      }
    }
  }
  #endif // FEATURE_MOON_TRACKING

  #ifdef FEATURE_SUN_TRACKING
  static byte sun_tracking_button_pushed = 0;
  static unsigned long last_time_sun_tracking_button_pushed = 0;
  if (sun_tracking_button) {
    if ((digitalReadEnhanced(sun_tracking_button) == BUTTON_ACTIVE_STATE)) {
      sun_tracking_button_pushed = 1;
      last_time_sun_tracking_button_pushed = millis();
      #ifdef DEBUG_BUTTONS
      debug_println("check_buttons: sun_tracking_button pushed");
      #endif // DEBUG_BUTTONS
    } else {
      if ((sun_tracking_button_pushed) && ((millis() - last_time_sun_tracking_button_pushed) >= 250)) {
        if (!sun_tracking_active) {
          #ifdef DEBUG_BUTTONS
          debug_println("check_buttons: sun tracking on");
          #endif // DEBUG_BUTTONS
          sun_tracking_active = 1;
          #ifdef FEATURE_MOON_TRACKING
          moon_tracking_active = 0;
          #endif // FEATURE_MOON_TRACKING          
        } else {
          #ifdef DEBUG_BUTTONS
          debug_print("check_buttons: sun tracking off");
          #endif // DEBUG_BUTTONS
          sun_tracking_active = 0;
        }
        sun_tracking_button_pushed = 0;
      }
    }
  }
  #endif // FEATURE_SUN_TRACKING

} /* check_buttons */
// --------------------------------------------------------------
#ifdef FEATURE_LCD_DISPLAY
char * idle_status(){


  #ifdef OPTION_DISPLAY_DIRECTION_STATUS
  return azimuth_direction(azimuth);
  #endif //OPTION_DISPLAY_DIRECTION_STATUS

  return("");



}


#endif //FEATURE_LCD_DISPLAY
// --------------------------------------------------------------

#if defined(FEATURE_LCD_DISPLAY) && defined(OPTION_DISPLAY_DIRECTION_STATUS)
char * azimuth_direction(int azimuth_in){

  azimuth_in = azimuth_in / HEADING_MULTIPLIER;



  if (azimuth_in > 348) {
    return N_STRING;
  }
  if (azimuth_in > 326) {
    return NNW_STRING;
  }
  if (azimuth_in > 303) {
    return NW_STRING;
  }
  if (azimuth_in > 281) {
    return WNW_STRING;
  }
  if (azimuth_in > 258) {
    return W_STRING;
  }
  if (azimuth_in > 236) {
    return WNW_STRING;
  }
  if (azimuth_in > 213) {
    return SW_STRING;
  }
  if (azimuth_in > 191) {
    return SSW_STRING;
  }
  if (azimuth_in > 168) {
    return S_STRING;
  }
  if (azimuth_in > 146) {
    return SSE_STRING;
  }
  if (azimuth_in > 123) {
    return SE_STRING;
  }
  if (azimuth_in > 101) {
    return ESE_STRING;
  }
  if (azimuth_in > 78) {
    return E_STRING;
  }
  if (azimuth_in > 56) {
    return ENE_STRING;
  }
  if (azimuth_in > 33) {
    return NE_STRING;
  }
  if (azimuth_in > 11) {
    return NNE_STRING;
  }
  return N_STRING;

} /* azimuth_direction */
#endif /* ifdef FEATURE_LCD_DISPLAY */
// --------------------------------------------------------------
#if defined(FEATURE_LCD_DISPLAY)
void update_display(){

  // update the LCD display

  static byte lcd_state_row_0 = 0;
  static byte lcd_state_row_1 = 0;

  static String row_0_string; // changed to static 2013-03-27 to see if it helps display stability

  static int last_azimuth = -1;

  static char workstring[10] = ""; // changed to static 2013-03-27 to see if it helps display stability

  unsigned int target = 0;

  #ifdef FEATURE_ELEVATION_CONTROL
  static int last_elevation = -1;
  static int last_target_elevation = 0;
  static byte last_el_state = 999;
  #endif

  byte decimal_places = 0;
  static byte last_az_state = 999;


  if ((lcd_state_row_0 == 0) && (lcd_state_row_1 == 0)){
    if (millis() < SPLASH_SCREEN_TIME){
      return;
    } else {
      lcd.clear();
      lcd_state_row_0 = LCD_IDLE_STATUS;
    }
  }


  // row 0 ------------------------------------------------------------

  #ifndef FEATURE_ELEVATION_CONTROL
  if (((millis() - last_lcd_update) > LCD_UPDATE_TIME) || (push_lcd_update) || (az_state != last_az_state)) {
  #else
  if (((millis() - last_lcd_update) > LCD_UPDATE_TIME) || (push_lcd_update) || (az_state != last_el_state) || (el_state != last_el_state)) {
  #endif

    #ifndef FEATURE_ELEVATION_CONTROL
    #ifdef FEATURE_AZ_PRESET_ENCODER
    target = az_encoder_raw_degrees;
    if (target > (359 * LCD_HEADING_MULTIPLIER)) {
      target = target - (360 * LCD_HEADING_MULTIPLIER);
    }
    if (target > (359 * LCD_HEADING_MULTIPLIER)) {
      target = target - (360 * LCD_HEADING_MULTIPLIER);
    }

    if (preset_encoders_state == ENCODER_AZ_PENDING) {
      clear_display_row(0);                                                 // Show Target Deg on upper line
      row_0_string = TARGET_STRING;
      dtostrf(target / LCD_HEADING_MULTIPLIER, 1, LCD_DECIMAL_PLACES, workstring);
      row_0_string.concat(workstring);
      row_0_string.concat(char(223));
      lcd.setCursor(((LCD_COLUMNS - row_0_string.length()) / 2), 0);
      lcd.print(row_0_string);

      lcd_state_row_0 = LCD_TARGET_AZ;
      #ifdef DEBUG_DISPLAY
      if (debug_mode) {
        control_port->print(F("update_display: "));
        control_port->println(row_0_string);
      }
      #endif // DEBUG_DISPLAY

    } else {
    #endif // FEATURE_AZ_PRESET_ENCODER

    if ((last_az_state != az_state) || (last_azimuth != azimuth)){
      if (az_state != IDLE) {
        if (az_request_queue_state == IN_PROGRESS_TO_TARGET) {
          clear_display_row(0);
          row_0_string = ROTATING_TO_STRING;
          dtostrf(target_azimuth / LCD_HEADING_MULTIPLIER, 1, LCD_DECIMAL_PLACES, workstring);
          row_0_string.concat(workstring);
          // row_0_string.concat(int(target_azimuth / LCD_HEADING_MULTIPLIER));
          row_0_string.concat(char(223));
          lcd.setCursor(((LCD_COLUMNS - row_0_string.length()) / 2), 0);
          lcd.print(row_0_string);
          lcd_state_row_0 = LCD_ROTATING_TO;

          #ifdef DEBUG_DISPLAY
          if (debug_mode) {
            control_port->print(F("update_display: "));
            control_port->println(row_0_string);
          }
          #endif // DEBUG_DISPLAY

        } else {

          if ((az_state == SLOW_START_CW) || (az_state == NORMAL_CW) || (az_state == SLOW_DOWN_CW) || (az_state == TIMED_SLOW_DOWN_CW)) {
            if (lcd_state_row_0 != LCD_ROTATING_CW) {
              clear_display_row(0);
              row_0_string = ROTATING_CW_STRING;
              lcd.setCursor(((LCD_COLUMNS - row_0_string.length()) / 2), 0);
              lcd.print(row_0_string);
              lcd_state_row_0 = LCD_ROTATING_CW;
              #ifdef DEBUG_DISPLAY
              if (debug_mode) {
                control_port->print(F("update_display: "));
                control_port->println(row_0_string);
              }
              #endif // DEBUG_DISPLAY
            }
          } else {
            if (lcd_state_row_0 != LCD_ROTATING_CCW) {
              clear_display_row(0);
              row_0_string = ROTATING_CCW_STRING;
              lcd.setCursor(((LCD_COLUMNS - row_0_string.length()) / 2), 0);
              lcd.print(row_0_string);
              lcd_state_row_0 = LCD_ROTATING_CCW;
              #ifdef DEBUG_DISPLAY
              if (debug_mode) {
                control_port->print(F("update_display: "));
                control_port->println(row_0_string);
              }
              #endif // DEBUG_DISPLAY
            }
          }
        }
      } else {   // az_state == IDLE


        #ifndef FEATURE_PARK // ---------
        if ((last_azimuth != azimuth) || (lcd_state_row_0 != LCD_IDLE_STATUS)) {
          row_0_string.concat(idle_status());
          if ((last_row_0_string == row_0_string) || (lcd_state_row_0 != LCD_IDLE_STATUS)) {
            clear_display_row(0);
            lcd_state_row_0 = LCD_IDLE_STATUS;
            #ifdef OPTION_DISPLAY_DIRECTION_STATUS
            lcd.setCursor(((LCD_COLUMNS - row_0_string.length()) / 2), 0);
            lcd.print(row_0_string);            
            #ifdef DEBUG_DISPLAY
            if (debug_mode) {
              control_port->print(F("update_display: "));
              control_port->println(row_0_string);
            }
            #endif // DEBUG_DISPLAY
            #endif //OPTION_DISPLAY_DIRECTION_STATUS
          } else {
            #ifdef OPTION_DISPLAY_DIRECTION_STATUS
            lcd.setCursor(((LCD_COLUMNS - row_0_string.length()) / 2) - 1, 0);
            lcd.print(" ");
            lcd.print(row_0_string);
            lcd.print(" ");
            #ifdef DEBUG_DISPLAY
            if (debug_mode) {
              control_port->print(F("update_display: "));
              control_port->println(row_0_string);
            }
            #endif // DEBUG_DISPLAY
            #endif //OPTION_DISPLAY_DIRECTION_STATUS
          }
        }

        #else // FEATURE_PARK-----------

        if (park_status == PARKED) {
          if (lcd_state_row_0 != LCD_PARKED) {
            row_0_string = PARKED_STRING;
            clear_display_row(0);
            lcd.setCursor(((LCD_COLUMNS - row_0_string.length()) / 2), 0);
            lcd.print(row_0_string);
            lcd_state_row_0 = LCD_PARKED;
          }

        } else {
          if ((last_azimuth != azimuth) || (lcd_state_row_0 != LCD_IDLE_STATUS)) {
            row_0_string.concat(idle_status());
            if ((last_row_0_string == row_0_string) || (lcd_state_row_0 != LCD_IDLE_STATUS)) {
              clear_display_row(0);
              lcd_state_row_0 = LCD_IDLE_STATUS;
              #ifdef OPTION_DISPLAY_DIRECTION_STATUS
              lcd.setCursor(((LCD_COLUMNS - row_0_string.length()) / 2), 0);
              lcd.print(row_0_string);            
              #ifdef DEBUG_DISPLAY
              if (debug_mode) {
                control_port->print(F("update_display: "));
                control_port->println(row_0_string);
              }
              #endif // DEBUG_DISPLAY
              #endif //OPTION_DISPLAY_DIRECTION_STATUS
            } else {
              #ifdef OPTION_DISPLAY_DIRECTION_STATUS
              lcd.setCursor(((LCD_COLUMNS - row_0_string.length()) / 2) - 1, 0);
              lcd.print(" ");
              lcd.print(row_0_string);
              lcd.print(" ");
              #ifdef DEBUG_DISPLAY
              if (debug_mode) {
                control_port->print(F("update_display: "));
                control_port->println(row_0_string);
              }
              #endif // DEBUG_DISPLAY
              #endif //OPTION_DISPLAY_DIRECTION_STATUS
            }
          }
        }
        #endif // FEATURE_PARK ---------





      }   // (az_state != IDLE)
      }//<---
    #ifdef FEATURE_AZ_PRESET_ENCODER
  }   // (preset_encoders_state == ENCODER_AZ_PENDING)
    #endif // FEATURE_AZ_PRESET_ENCODER
    #endif /* ifndef FEATURE_ELEVATION_CONTROL */

    // ------------ AZ & EL -----------------------------------------------

    #ifdef FEATURE_ELEVATION_CONTROL

    #ifdef FEATURE_AZ_PRESET_ENCODER
    #ifndef FEATURE_EL_PRESET_ENCODER

    unsigned int target = az_encoder_raw_degrees;
    if (target > (359 * LCD_HEADING_MULTIPLIER)) {
      target = target - (360 * LCD_HEADING_MULTIPLIER);
    }
    if (target > (359 * LCD_HEADING_MULTIPLIER)) {
      target = target - (360 * LCD_HEADING_MULTIPLIER);
    }

    if (preset_encoders_state == ENCODER_AZ_PENDING) {
      clear_display_row(0);                                                 // Show Target Deg on upper line
      row_0_string = TARGET_STRING;
      dtostrf(target / LCD_HEADING_MULTIPLIER, 1, LCD_DECIMAL_PLACES, workstring);
      row_0_string.concat(workstring);
      lcd.setCursor(((LCD_COLUMNS - row_0_string.length()) / 2), 0);
      lcd.print(row_0_string);

      lcd_state_row_0 = LCD_TARGET_AZ;
      #ifdef DEBUG_DISPLAY
      if (debug_mode) {
        control_port->print(F("update_display: "));
        control_port->println(row_0_string);
      }
      #endif // DEBUG_DISPLAY

    } else {

    #endif // ndef FEATURE_EL_PRESET_ENCODER
    #endif // FEATURE_AZ_PRESET_ENCODER


    #ifdef FEATURE_EL_PRESET_ENCODER
    unsigned int target = az_encoder_raw_degrees;
    if (target > (359 * LCD_HEADING_MULTIPLIER)) {
      target = target - (360 * LCD_HEADING_MULTIPLIER);
    }
    if (target > (359 * LCD_HEADING_MULTIPLIER)) {
      target = target - (360 * LCD_HEADING_MULTIPLIER);
    }

    if (preset_encoders_state != ENCODER_IDLE) {
      switch (preset_encoders_state) {
        case ENCODER_AZ_PENDING:
          clear_display_row(0);                                                 // Show Target Deg on upper line
          row_0_string = AZ_TARGET_STRING;
          dtostrf(target / LCD_HEADING_MULTIPLIER, 1, LCD_DECIMAL_PLACES, workstring);
          row_0_string.concat(workstring);
          row_0_string.concat(char(223));
          lcd.setCursor(((LCD_COLUMNS - row_0_string.length()) / 2), 0);
          lcd.print(row_0_string);
          lcd_state_row_0 = LCD_TARGET_AZ;
          break;
        case ENCODER_EL_PENDING:
          clear_display_row(0);                                                 // Show Target Deg on upper line
          row_0_string = EL_TARGET_STRING;
          dtostrf(el_encoder_degrees / LCD_HEADING_MULTIPLIER, 1, LCD_DECIMAL_PLACES, workstring);
          row_0_string.concat(workstring);
          row_0_string.concat(char(223));
          lcd.setCursor(((LCD_COLUMNS - row_0_string.length()) / 2), 0);
          lcd.print(row_0_string);
          lcd_state_row_0 = LCD_TARGET_EL;
          break;
        case ENCODER_AZ_EL_PENDING:
          clear_display_row(0);                                                 // Show Target Deg on upper line
          row_0_string = TARGET_STRING;
          dtostrf(target / LCD_HEADING_MULTIPLIER, 1, LCD_DECIMAL_PLACES, workstring);
          row_0_string.concat(workstring);
          row_0_string.concat(char(223));
          dtostrf(el_encoder_degrees / LCD_HEADING_MULTIPLIER, 1, LCD_DECIMAL_PLACES, workstring);
          row_0_string.concat("   ");
          row_0_string.concat(workstring);
          row_0_string.concat(char(223));
          lcd.setCursor(((LCD_COLUMNS - row_0_string.length()) / 2), 0);
          lcd.print(row_0_string);
          lcd_state_row_0 = LCD_TARGET_AZ_EL;
          break;
      } /* switch */
    } else { // (preset_encoders_state != ENCODER_IDLE)
    #endif // FEATURE_EL_PRESET_ENCODER


  if ((az_state != last_az_state) || (el_state != last_el_state) || push_lcd_update || (last_azimuth != azimuth)){

    if ((az_state != IDLE) && (el_state == IDLE)) {
      if (az_request_queue_state == IN_PROGRESS_TO_TARGET) {
        clear_display_row(0);
        row_0_string = ROTATING_TO_STRING;
        dtostrf(target_azimuth / LCD_HEADING_MULTIPLIER, 1, LCD_DECIMAL_PLACES, workstring);
        row_0_string.concat(workstring);
        row_0_string.concat(char(223));
        lcd.setCursor(((LCD_COLUMNS - row_0_string.length()) / 2), 0);
        lcd.print(row_0_string);
        lcd_state_row_0 = LCD_ROTATING_TO;
      } else {
        if ((az_state == SLOW_START_CW) || (az_state == NORMAL_CW) || (az_state == SLOW_DOWN_CW) || (az_state == TIMED_SLOW_DOWN_CW)) {
          clear_display_row(0);
          row_0_string = ROTATING_CW_STRING;
          lcd.setCursor(((LCD_COLUMNS - row_0_string.length()) / 2), 0);
          lcd.print(row_0_string);
          lcd_state_row_0 = LCD_ROTATING_CW;
        } else {
          clear_display_row(0);
          row_0_string = ROTATING_CCW_STRING;
          lcd.setCursor(((LCD_COLUMNS - row_0_string.length()) / 2), 0);
          lcd.print(row_0_string);
          lcd_state_row_0 = LCD_ROTATING_CCW;
        }
      }
    }   // ((az_state != IDLE) && (el_state == IDLE))

    if ((az_state == IDLE) && (el_state != IDLE)) {
      // if ((el_request_queue_state == IN_PROGRESS_TO_TARGET) && ((lcd_state_row_0 != LCD_ELEVATING_TO) || (target_elevation != last_target_elevation))){
      if (el_request_queue_state == IN_PROGRESS_TO_TARGET) {
        if ((lcd_state_row_0 != LCD_ELEVATING_TO) || (target_elevation != last_target_elevation)) {
          clear_display_row(0);
          row_0_string = ELEVATING_TO_STRING;
          dtostrf(target_elevation / LCD_HEADING_MULTIPLIER, 1, LCD_DECIMAL_PLACES, workstring);
          row_0_string.concat(workstring);
          row_0_string.concat(char(223));
          lcd.setCursor(((LCD_COLUMNS - row_0_string.length()) / 2), 0);
          lcd.print(row_0_string);
          lcd_state_row_0 = LCD_ELEVATING_TO;
        }
      } else {
        if (((el_state == SLOW_START_UP) || (el_state == NORMAL_UP) || (el_state == SLOW_DOWN_UP) || (el_state == TIMED_SLOW_DOWN_UP)) && (lcd_state_row_0 != LCD_ELEVATING_UP)) {
          clear_display_row(0);
          row_0_string = ELEVATING_UP_STRING;
          lcd.setCursor(((LCD_COLUMNS - row_0_string.length()) / 2), 0);
          lcd.print(row_0_string);
          lcd_state_row_0 = LCD_ELEVATING_UP;
        }
        if (((el_state == SLOW_START_DOWN) || (el_state == NORMAL_DOWN) || (el_state == SLOW_DOWN_DOWN) || (el_state == TIMED_SLOW_DOWN_DOWN)) && (lcd_state_row_0 != LCD_ELEVATING_DOWN)) {
          clear_display_row(0);
          row_0_string = ELEVATING_DOWN_STRING;
          lcd.setCursor(((LCD_COLUMNS - row_0_string.length()) / 2), 0);
          lcd.print(row_0_string);
          lcd_state_row_0 = LCD_ELEVATING_DOWN;
        }
      }
    }   // ((az_state == IDLE) && (el_state != IDLE))

    if ((az_state != IDLE) && (el_state != IDLE) && (lcd_state_row_0 != LCD_ROTATING_AZ_EL)) {
      clear_display_row(0);
      row_0_string = ROTATING_STRING;
      if (az_request_queue_state == NONE) {
        if (current_az_state() == ROTATING_CW) {
          row_0_string.concat(CW_STRING);
        } else {
          row_0_string.concat(CCW_STRING);
        }
      } else {
        //row_0_string.concat(int(target_azimuth / LCD_HEADING_MULTIPLIER));
        if ((target_azimuth / LCD_HEADING_MULTIPLIER) < 100){decimal_places = LCD_DECIMAL_PLACES;} else {decimal_places = 0;}
        dtostrf(target_azimuth / LCD_HEADING_MULTIPLIER, 1, decimal_places, workstring);
        row_0_string.concat(workstring);        
      }
      row_0_string.concat(" ");
      if (el_request_queue_state == NONE) {
        if (current_el_state() == ROTATING_UP) {
          row_0_string.concat(UP_STRING);
        } else {
          row_0_string.concat(DOWN_STRING);
        }
      } else {
        if ((target_elevation / LCD_HEADING_MULTIPLIER) < 100){decimal_places = LCD_DECIMAL_PLACES;} else {decimal_places = 0;}
        dtostrf(target_elevation / LCD_HEADING_MULTIPLIER, 1, decimal_places, workstring);
        row_0_string.concat(workstring);
      }
      lcd_state_row_0 = LCD_ROTATING_AZ_EL;
      lcd.setCursor(((LCD_COLUMNS - row_0_string.length()) / 2), 0);
      lcd.print(row_0_string);
    }   // ((az_state != IDLE) && (el_state != IDLE))

    if ((az_state == IDLE) && (el_state == IDLE)) {

      #ifndef FEATURE_PARK
      if ((last_azimuth != azimuth) || (lcd_state_row_0 != LCD_IDLE_STATUS)) {
        row_0_string.concat(idle_status());
        if ((last_row_0_string != row_0_string) || (lcd_state_row_0 != LCD_IDLE_STATUS)) {
          clear_display_row(0);
          lcd_state_row_0 = LCD_IDLE_STATUS;          
          #ifdef OPTION_DISPLAY_DIRECTION_STATUS
          lcd.setCursor(((LCD_COLUMNS - row_0_string.length()) / 2), 0);
          lcd.print(row_0_string);
          #endif //OPTION_DISPLAY_DIRECTION_STATUS
        } else {
          #ifdef OPTION_DISPLAY_DIRECTION_STATUS
          lcd.setCursor(((LCD_COLUMNS - row_0_string.length()) / 2) - 1, 0);
          lcd.print(" ");
          lcd.print(row_0_string);
          lcd.print(" ");
          #endif //OPTION_DISPLAY_DIRECTION_STATUS
        }
      }
      #else // FEATURE_PARK


      if (park_status == PARKED) {
        if (lcd_state_row_0 != LCD_PARKED) {
          row_0_string = PARKED_STRING;
          clear_display_row(0);
          lcd.setCursor(((LCD_COLUMNS - row_0_string.length()) / 2), 0);
          lcd.print(row_0_string);
          lcd_state_row_0 = LCD_PARKED;
        }
      } else {
        if ((last_azimuth != azimuth) || (lcd_state_row_0 != LCD_IDLE_STATUS)) {
          row_0_string.concat(idle_status());
          if ((last_row_0_string == row_0_string) || (lcd_state_row_0 != LCD_IDLE_STATUS)) {
            clear_display_row(0);
            lcd_state_row_0 = LCD_IDLE_STATUS;
            #ifdef OPTION_DISPLAY_DIRECTION_STATUS       
            lcd.setCursor(((LCD_COLUMNS - row_0_string.length()) / 2), 0);
            lcd.print(row_0_string);
            #endif //OPTION_DISPLAY_DIRECTION_STATUS
          } else {
            #ifdef OPTION_DISPLAY_DIRECTION_STATUS
            lcd.setCursor(((LCD_COLUMNS - row_0_string.length()) / 2) - 1, 0);
            lcd.print(" ");
            lcd.print(row_0_string);
            lcd.print(" ");
            #endif //OPTION_DISPLAY_DIRECTION_STATUS
          }
        }
      }
      #endif // FEATURE_PARK


    }   // ((az_state == IDLE) && (el_state == IDLE))

      #ifdef FEATURE_EL_PRESET_ENCODER
  }   // (preset_encoders_state != ENCODER_IDLE)
    #endif // FEATURE_EL_PRESET_ENCODER



    #ifdef FEATURE_AZ_PRESET_ENCODER
    #ifndef FEATURE_EL_PRESET_ENCODER
  }
  #endif // ndef FEATURE_EL_PRESET_ENCODER
  #endif // FEATURE_AZ_PRESET_ENCODER

  } //<--

    #endif // FEATURE_ELEVATION_CONTROL



    push_lcd_update = 0;
    last_az_state = az_state;
    #ifdef FEATURE_ELEVATION_CONTROL
    last_el_state = el_state;
    #endif // FEATURE_ELEVATION_CONTROL

  }


  //     row 1 --------------------------------------------


  if ((millis() - last_lcd_update) > LCD_UPDATE_TIME) {
    #ifndef FEATURE_ELEVATION_CONTROL // ---------------- az only -----------------------------------
    if (last_azimuth != azimuth) {
      //clear_display_row(1);
      row_0_string = AZIMUTH_STRING;
      dtostrf(azimuth / LCD_HEADING_MULTIPLIER, 1, LCD_DECIMAL_PLACES, workstring);
      row_0_string.concat(workstring);
      row_0_string.concat(char(223));
      //lcd.setCursor(((LCD_COLUMNS - row_0_string.length()) / 2), 1);
      lcd.setCursor(0,1);
      int x = LCD_COLUMNS - row_0_string.length();
      byte y = 0;
      while (x > 0){
        if ((y%2) != 0){
          row_0_string = " " + row_0_string;
        } else {
          row_0_string.concat(" ");
        }
        y++;
        x--;
      }      
      lcd.print(row_0_string);
      last_azimuth = azimuth;
      lcd_state_row_1 = LCD_HEADING;
    }
    #endif // FEATURE_ELEVATION_CONTROL---------------------------------------------------------------




    #ifdef FEATURE_ELEVATION_CONTROL  // --------------------az & el---------------------------------
    if ((last_azimuth != azimuth) || (last_elevation != elevation)) {
      //clear_display_row(1);
      #if defined(FEATURE_ONE_DECIMAL_PLACE_HEADINGS) || defined(FEATURE_TWO_DECIMAL_PLACE_HEADINGS)
      if ((azimuth >= 1000) && (elevation >= 1000)) {
        row_0_string = AZ_STRING;
      } else {
        row_0_string = AZ_SPACE_STRING;
      }
      #else
      row_0_string = AZ_SPACE_STRING;
      #endif // efined(FEATURE_ONE_DECIMAL_PLACE_HEADINGS) || defined(FEATURE_TWO_DECIMAL_PLACE_HEADINGS)
      dtostrf(azimuth/ LCD_HEADING_MULTIPLIER, 3, LCD_DECIMAL_PLACES, workstring);
      row_0_string.concat(workstring);

      #if !defined(FEATURE_ONE_DECIMAL_PLACE_HEADINGS) && !defined(FEATURE_TWO_DECIMAL_PLACE_HEADINGS)
      if (LCD_COLUMNS > 14) {
        row_0_string.concat(char(223));
      }
      #else
      if ((LCD_COLUMNS > 18) || ((azimuth < 100) && (elevation < 100))) {
        row_0_string.concat(char(223));
      }
      #endif

      #if defined(FEATURE_ONE_DECIMAL_PLACE_HEADINGS) || defined(FEATURE_TWO_DECIMAL_PLACE_HEADINGS)
      if ((elevation >= 1000) && (azimuth >= 1000)) {
        row_0_string.concat(SPACE_EL_STRING);
      } else {
        row_0_string.concat(SPACE_EL_SPACE_STRING);
      }
      #else
      row_0_string.concat(SPACE_EL_SPACE_STRING);
      #endif // defined(FEATURE_ONE_DECIMAL_PLACE_HEADINGS) || defined(FEATURE_TWO_DECIMAL_PLACE_HEADINGS)

      dtostrf(elevation / LCD_HEADING_MULTIPLIER, 1, LCD_DECIMAL_PLACES, workstring);
      row_0_string.concat(workstring);

      #if !defined(FEATURE_ONE_DECIMAL_PLACE_HEADINGS) && !defined(FEATURE_TWO_DECIMAL_PLACE_HEADINGS)
      if (LCD_COLUMNS > 14) {
        row_0_string.concat(char(223));
      }
      #else
      if ((LCD_COLUMNS > 18) || ((azimuth < 100) && (elevation < 100))) {
        row_0_string.concat(char(223));
      }
      #endif

      lcd.setCursor(0,1);
      int x = LCD_COLUMNS - row_0_string.length();
      byte y = 0;
      while (x > 0){
        if ((y%2) != 0){
          row_0_string = " " + row_0_string;
        } else {
          row_0_string.concat(" ");
        }
        y++;
        x--;
      }
      //lcd.setCursor(((LCD_COLUMNS - row_0_string.length()) / 2), 1);
      lcd.print(row_0_string);

      last_azimuth = azimuth;
      last_elevation = elevation;
      lcd_state_row_1 = LCD_HEADING;
    }
        #endif // FEATURE_ELEVATION_CONTROL //------------------------------------------------------------



  }


  char temp_string[10] = "";

  // clock display -- small ----------------------------------------------------------
  #if defined(OPTION_DISPLAY_HHMMSS_CLOCK) && defined(FEATURE_CLOCK)

  
  char clock_temp_string[9] = "";
  static byte last_clock_seconds = 0;
  static byte last_clock_lcd_row_state = 0;
  byte clock_digits = 0;


  update_time();
  if (((last_clock_seconds != clock_seconds) || (last_clock_lcd_row_state != LCD_IDLE_STATUS)) && (lcd_state_row_0 == LCD_IDLE_STATUS)){
    #ifdef OPTION_CLOCK_ALWAYS_HAVE_HOUR_LEADING_ZERO
    if (clock_hours < 10) {
      strcat(clock_temp_string, "0");
    }    
    dtostrf(clock_hours, 0, 0, temp_string);
    strcat(clock_temp_string,temp_string);    
    #else    
    dtostrf(clock_hours, 0, 0, temp_string);
    strcpy(clock_temp_string,temp_string);
    #endif //OPTION_CLOCK_ALWAYS_HAVE_HOUR_LEADING_ZERO
    strcat(clock_temp_string,":");
    if (clock_minutes < 10) {
      strcat(clock_temp_string, "0");
    }
    if (clock_hours > 9){
      clock_digits = 1;
    }
    dtostrf(clock_minutes, 0, 0, temp_string);
    strcat(clock_temp_string,temp_string);
    strcat(clock_temp_string,":");
    if (clock_seconds < 10) {
      strcat(clock_temp_string, "0");
    }
    dtostrf(clock_seconds, 0, 0, temp_string);
    strcat(clock_temp_string,temp_string);
    if (LCD_HHMMSS_CLOCK_POSITION == LEFT){
      lcd.setCursor(0,0);
    } else {
      lcd.setCursor(LCD_COLUMNS-(7+clock_digits),0);
    }
    lcd.print(clock_temp_string);
    if ((clock_minutes == 0) && (clock_hours == 0)) {
      if (LCD_HHMMSS_CLOCK_POSITION == RIGHT){
        lcd.setCursor(LCD_COLUMNS-(9+clock_digits),0);
      }
      lcd.print("  ");
    }
    last_clock_seconds = clock_seconds;
    
  }
  last_clock_lcd_row_state = lcd_state_row_0;
  #endif //defined(OPTION_DISPLAY_HHMMSS_CLOCK) && defined(FEATURE_CLOCK)



  // clock display -- small ----------------------------------------------------------
  #if defined(OPTION_DISPLAY_HHMM_CLOCK) && defined(FEATURE_CLOCK)

  
  char clock_temp_string[10] = "";
  static byte last_clock_minutes = 0;
  static byte last_clock_lcd_row_state = 0;
  byte clock_digits = 0;


  update_time();
  if (((last_clock_minutes != clock_minutes) || (last_clock_lcd_row_state != LCD_IDLE_STATUS)) && (lcd_state_row_0 == LCD_IDLE_STATUS)){
    #ifdef OPTION_CLOCK_ALWAYS_HAVE_HOUR_LEADING_ZERO
    if (clock_hours < 10) {
      strcat(clock_temp_string, "0");
    }    
    dtostrf(clock_hours, 0, 0, temp_string);
    strcat(clock_temp_string,temp_string);    
    #else
    dtostrf(clock_hours, 0, 0, temp_string);
    strcpy(clock_temp_string,temp_string);
    #endif //OPTION_CLOCK_ALWAYS_HAVE_HOUR_LEADING_ZERO
    strcat(clock_temp_string,":");
    if (clock_minutes < 10) {
      strcat(clock_temp_string, "0");
    }
    if (clock_hours > 9){
      clock_digits = 1;
    }
    dtostrf(clock_minutes, 0, 0, temp_string);
    strcat(clock_temp_string,temp_string);
    if (LCD_HHMM_CLOCK_POSITION == LEFT){
      lcd.setCursor(0,0);
    } else {
      lcd.setCursor(LCD_COLUMNS-(4+clock_digits),0);
    }
    lcd.print(clock_temp_string);
    if ((clock_minutes == 0) && (clock_hours == 0)) {
      if (LCD_HHMM_CLOCK_POSITION == RIGHT){
        lcd.setCursor(LCD_COLUMNS-(6+clock_digits),0);
      }
      lcd.print("  ");
    }
    last_clock_minutes = clock_minutes;
    
  }
  last_clock_lcd_row_state = lcd_state_row_0;
  #endif //defined(OPTION_DISPLAY_HHMM_CLOCK) && defined(FEATURE_CLOCK)


// alternating H:MM clock and maidenhead display ------------------------------------------------------------
  #if defined(OPTION_DISPLAY_ALT_HHMM_CLOCK_AND_MAIDENHEAD) && defined(FEATURE_CLOCK)

  
  char clock_temp_string[10] = "";
  static byte last_clock_minutes = 0;
  static byte last_clock_seconds = 0;
  static byte last_clock_lcd_row_state = 0;
  byte clock_digits = 0;
  static byte displaying_clock = 1;


  if ((lcd_state_row_0 != LCD_UNDEF) && ((lcd_state_row_0 == LCD_IDLE_STATUS) || ((LCD_ALT_HHMM_CLOCK_AND_MAIDENHEAD_ROW-1)!= 0))){
    update_time();

    if (((last_clock_seconds != clock_seconds) || ((((LCD_ALT_HHMM_CLOCK_AND_MAIDENHEAD_ROW-1)== 0))) && (last_clock_lcd_row_state != LCD_IDLE_STATUS)) && ((lcd_state_row_0 == LCD_IDLE_STATUS) || ((LCD_ALT_HHMM_CLOCK_AND_MAIDENHEAD_ROW-1)!= 0))){
      if (((clock_seconds % 5) == 0) || (((LCD_ALT_HHMM_CLOCK_AND_MAIDENHEAD_ROW-1)== 0) && (last_clock_lcd_row_state != LCD_IDLE_STATUS))) {
        if (displaying_clock){
          if (LCD_ALT_HHMM_CLOCK_AND_MAIDENHEAD_POSITION == LEFT){
            lcd.setCursor(0,LCD_ALT_HHMM_CLOCK_AND_MAIDENHEAD_ROW-1);        
          } else {
            lcd.setCursor(LCD_COLUMNS-6,LCD_ALT_HHMM_CLOCK_AND_MAIDENHEAD_ROW-1); 
          }
          lcd.print(coordinates_to_maidenhead(latitude,longitude));
          displaying_clock = 0;
        } else {
          displaying_clock = 2;  // switch to the clock (2 == print the clock regardless of time)
        }

      }
      last_clock_seconds = clock_seconds;
    }


    if (((displaying_clock && (last_clock_minutes != clock_minutes)) || (displaying_clock == 2)) && ((lcd_state_row_0 == LCD_IDLE_STATUS) || ((LCD_ALT_HHMM_CLOCK_AND_MAIDENHEAD_ROW-1)!= 0))){
      #ifdef OPTION_CLOCK_ALWAYS_HAVE_HOUR_LEADING_ZERO
      if (clock_hours < 10) {
        strcpy(clock_temp_string, "0");
      }    
      dtostrf(clock_hours, 0, 0, temp_string);
      strcat(clock_temp_string,temp_string);    
      #else          
      dtostrf(clock_hours, 0, 0, temp_string);
      strcpy(clock_temp_string,temp_string);
      #endif //OPTION_CLOCK_ALWAYS_HAVE_HOUR_LEADING_ZERO
      strcat(clock_temp_string,":");
      if (clock_minutes < 10) {
        strcat(clock_temp_string, "0");
      }
      if (clock_hours > 9){
        clock_digits = 1;
      }
      dtostrf(clock_minutes, 0, 0, temp_string);
      strcat(clock_temp_string,temp_string);
      if (LCD_ALT_HHMM_CLOCK_AND_MAIDENHEAD_POSITION == LEFT){
        lcd.setCursor(0,LCD_ALT_HHMM_CLOCK_AND_MAIDENHEAD_ROW-1);
      } else {
        lcd.setCursor(LCD_COLUMNS-(4+clock_digits),LCD_ALT_HHMM_CLOCK_AND_MAIDENHEAD_ROW-1);
      }
      lcd.print(clock_temp_string);
      if (LCD_ALT_HHMM_CLOCK_AND_MAIDENHEAD_POSITION == RIGHT){
        lcd.setCursor(LCD_COLUMNS-(6+clock_digits),LCD_ALT_HHMM_CLOCK_AND_MAIDENHEAD_ROW-1);
      }
      lcd.print("  ");
      last_clock_minutes = clock_minutes;
      displaying_clock = 1;
    }
    last_clock_lcd_row_state = lcd_state_row_0;
  }
  #endif //defined(OPTION_DISPLAY_ALT_HHMM_CLOCK_AND_MAIDENHEAD) && defined(FEATURE_CLOCK)

  // constant HH:MM:SS clock and maidenhead display  ----------------------------------------------------------
  #if defined(OPTION_DISPLAY_CONSTANT_HHMMSS_CLOCK_AND_MAIDENHEAD) && defined(FEATURE_CLOCK)

  
  char clock_temp_string[14] = "";
  static byte last_clock_minutes = 0;
  static byte last_clock_seconds = 0;
  static byte last_clock_lcd_row_state = 0;
  byte clock_digits = 0;


  if ((lcd_state_row_0 != LCD_UNDEF) && ((lcd_state_row_0 == LCD_IDLE_STATUS) || ((LCD_CONSTANT_HHMMSS_CLOCK_AND_MAIDENHEAD_ROW-1)!= 0))){
    update_time();

    if (((last_clock_seconds != clock_seconds) || ((((LCD_CONSTANT_HHMMSS_CLOCK_AND_MAIDENHEAD_ROW-1)== 0))) && (last_clock_lcd_row_state != LCD_IDLE_STATUS)) && ((lcd_state_row_0 == LCD_IDLE_STATUS) || ((LCD_CONSTANT_HHMMSS_CLOCK_AND_MAIDENHEAD_ROW-1)!= 0))){
      if (LCD_CONSTANT_HHMMSS_CLOCK_AND_MAIDENHEAD_POSITION == LEFT){
        lcd.setCursor(0,LCD_CONSTANT_HHMMSS_CLOCK_AND_MAIDENHEAD_ROW-1);        
      } else {
        lcd.setCursor(LCD_COLUMNS-14,LCD_CONSTANT_HHMMSS_CLOCK_AND_MAIDENHEAD_ROW-1); 
      }
      #ifdef OPTION_CLOCK_ALWAYS_HAVE_HOUR_LEADING_ZERO
      if (clock_hours < 10) {
        strcpy(clock_temp_string, "0");
      }    
      dtostrf(clock_hours, 0, 0, temp_string);
      strcat(clock_temp_string,temp_string);    
      #else          
      dtostrf(clock_hours, 0, 0, temp_string);
      strcpy(clock_temp_string,temp_string);
      #endif //OPTION_CLOCK_ALWAYS_HAVE_HOUR_LEADING_ZERO
      strcat(clock_temp_string,":");
      if (clock_minutes < 10) {
        strcat(clock_temp_string, "0");
      }
      if (clock_hours > 9){
        clock_digits = 1;
      }
      dtostrf(clock_minutes, 0, 0, temp_string);
      strcat(clock_temp_string,temp_string);
      strcat(clock_temp_string,":");
      if (clock_seconds < 10) {
        strcat(clock_temp_string, "0");
      }
      dtostrf(clock_seconds, 0, 0, temp_string);
      strcat(clock_temp_string,temp_string);
      lcd.print(clock_temp_string);
      lcd.print(" ");
      lcd.print(coordinates_to_maidenhead(latitude,longitude));
      last_clock_seconds = clock_seconds;
    }


    last_clock_lcd_row_state = lcd_state_row_0;
  }
  #endif //defined(OPTION_DISPLAY_CONSTANT_HHMMSS_CLOCK_AND_MAIDENHEAD) && defined(FEATURE_CLOCK)
 
  // gps indicator -------------------------------------------------------------------------
  #if defined(OPTION_DISPLAY_GPS_INDICATOR) && defined(FEATURE_GPS) && defined(FEATURE_CLOCK)
  static byte last_clock_status = FREE_RUNNING;
  static byte last_gps_lcd_row_state = 0;

  if ((lcd_state_row_0 != LCD_UNDEF) && ((lcd_state_row_0 == LCD_IDLE_STATUS) || ((LCD_GPS_INDICATOR_ROW-1)!= 0))){
    if ((last_clock_status != clock_status) || ((LCD_GPS_INDICATOR_ROW == 1) && (last_gps_lcd_row_state != lcd_state_row_0))) {
      if (LCD_GPS_INDICATOR_POSITION == LEFT){
        lcd.setCursor(0,LCD_GPS_INDICATOR_ROW-1);
      } else {
        lcd.setCursor(LCD_COLUMNS-3,LCD_GPS_INDICATOR_ROW-1);
      }
      if ((clock_status == GPS_SYNC) || (clock_status == SLAVE_SYNC_GPS)){
        lcd.print(GPS_STRING);
      } else {
        lcd.print("   ");
      }
    }
    last_clock_status = clock_status;
  }
  last_gps_lcd_row_state = lcd_state_row_0;
  #endif //defined(OPTION_DISPLAY_GPS_INDICATOR) && defined(FEATURE_GPS)  && defined(FEATURE_CLOCK)




  #if (defined(OPTION_DISPLAY_MOON_TRACKING_CONTINUOUSLY) && defined(FEATURE_MOON_TRACKING)) || (defined(OPTION_DISPLAY_SUN_TRACKING_CONTINUOUSLY) && defined(FEATURE_SUN_TRACKING)) || defined(OPTION_DISPLAY_MOON_OR_SUN_TRACKING_CONDITIONAL)
  char trackingstring[LCD_COLUMNS+2] = "";
  char temptrackingstring[LCD_COLUMNS+2] = "";
  #endif

  //  moon tracking ----------------------------------------------------------
  #if defined(OPTION_DISPLAY_MOON_TRACKING_CONTINUOUSLY) && defined(FEATURE_MOON_TRACKING)

  static unsigned long last_moon_tracking_check_time = 0;
  static byte last_strlen_moon = 0;

  if ((lcd_state_row_0 != 0) && (lcd_state_row_1 != 0) && ((millis()-last_moon_tracking_check_time) > LCD_MOON_TRACKING_UPDATE_INTERVAL)) {  
    update_moon_position();
    strcpy(trackingstring,"");
    if (moon_tracking_active){
      if (moon_visible){
        strcat(trackingstring,TRACKING_ACTIVE_CHAR);
      } else {
        strcat(trackingstring,"-");
      }
    }
    strcat(trackingstring,MOON_STRING);
    dtostrf(moon_azimuth,0,LCD_DECIMAL_PLACES,temp_string);
    strcat(trackingstring,temp_string);
    if ((LCD_COLUMNS>16) && ((moon_azimuth < 100) || (abs(moon_elevation)<100))) {strcat(trackingstring,"\xDF");}
    strcat(trackingstring," ");
    dtostrf(moon_elevation,0,LCD_DECIMAL_PLACES,temp_string);
    strcat(trackingstring,temp_string);
    if ((LCD_COLUMNS>16) && ((moon_azimuth < 100) || (abs(moon_elevation)<100))) {strcat(trackingstring,"\xDF");}
    if (moon_tracking_active){
      if (moon_visible){
        strcat(trackingstring,TRACKING_ACTIVE_CHAR);
      } else {
        strcat(trackingstring,TRACKING_INACTIVE_CHAR);
      }
    }
    // if (strlen(trackingstring) < last_strlen_moon){
    //   clear_display_row(LCD_MOON_TRACKING_ROW-1);
    // }
    // last_strlen_moon = strlen(trackingstring);
    // lcd.setCursor((LCD_COLUMNS-strlen(trackingstring))/2,LCD_MOON_TRACKING_ROW-1);

    lcd.setCursor(0,LCD_MOON_TRACKING_ROW-1);
    int x = LCD_COLUMNS - strlen(trackingstring);
    byte y = 0;
    while (x > 0){
      if ((y%2) != 0){
        strcpy(temptrackingstring," ");
        strcat(temptrackingstring,trackingstring);
        strcpy(trackingstring,temptrackingstring);
      } else {
        strcat(trackingstring," ");
      }
      y++;
      x--;
    }



    lcd.print(trackingstring);
    last_moon_tracking_check_time = millis();
  }
  #endif //defined(OPTION_DISPLAY_MOON_TRACKING_CONTINUOUSLY) && defined(FEATURE_MOON_TRACKING)


  //  sun tracking ----------------------------------------------------------
  #if defined(OPTION_DISPLAY_SUN_TRACKING_CONTINUOUSLY) && defined(FEATURE_SUN_TRACKING)

  static unsigned long last_sun_tracking_check_time = 0;
  static byte last_strlen_sun = 0;

  if ((lcd_state_row_0 != 0) && (lcd_state_row_1 != 0) && ((millis()-last_sun_tracking_check_time) > LCD_SUN_TRACKING_UPDATE_INTERVAL)) {  
    update_sun_position();
    strcpy(trackingstring,"");
    if (sun_tracking_active){
      if (sun_visible){
        strcat(trackingstring,TRACKING_ACTIVE_CHAR);
      } else {
        strcat(trackingstring,TRACKING_INACTIVE_CHAR);
      }
    }
    strcat(trackingstring,SUN_STRING);
    dtostrf(sun_azimuth,0,LCD_DECIMAL_PLACES,temp_string);
    strcat(trackingstring,temp_string);
    if ((LCD_COLUMNS>16) && ((sun_azimuth < 100) || (abs(sun_elevation)<100))) {strcat(trackingstring,"\xDF");}
    strcat(trackingstring," ");
    dtostrf(sun_elevation,0,LCD_DECIMAL_PLACES,temp_string);
    strcat(trackingstring,temp_string);
    if ((LCD_COLUMNS>16) && ((sun_azimuth < 100) || (abs(sun_elevation)<100))) {strcat(trackingstring,"\xDF");}
    if (sun_tracking_active){
      if (sun_visible){
        strcat(trackingstring,TRACKING_ACTIVE_CHAR);
      } else {
        strcat(trackingstring,TRACKING_INACTIVE_CHAR);
      }
    }
    // if (strlen(trackingstring) < last_strlen_sun){
    //   clear_display_row(LCD_SUN_TRACKING_ROW-1);
    // }
    // last_strlen_sun = strlen(trackingstring);
    // lcd.setCursor((LCD_COLUMNS-strlen(trackingstring))/2,LCD_SUN_TRACKING_ROW-1);

    lcd.setCursor(0,LCD_SUN_TRACKING_ROW-1);
    int x = LCD_COLUMNS - strlen(trackingstring);
    byte y = 0;
    while (x > 0){
      if ((y%2) != 0){
        strcpy(temptrackingstring," ");
        strcat(temptrackingstring,trackingstring);
        strcpy(trackingstring,temptrackingstring);
      } else {
        strcat(trackingstring," ");
      }
      y++;
      x--;
    }



    lcd.print(trackingstring);
    last_sun_tracking_check_time = millis();
  }
  #endif //defined(OPTION_DISPLAY_SUN_TRACKING_CONTINUOUSLY) && defined(FEATURE_SUN_TRACKING)






  // moon and/or sun tracking conditional ------------------------------------------------------------------------
  #ifdef OPTION_DISPLAY_MOON_OR_SUN_TRACKING_CONDITIONAL

  //  moon tracking ----
  #if defined(FEATURE_MOON_TRACKING)

  static unsigned long last_moon_tracking_check_time = 0;
  static byte last_strlen_moon = 0;
  static byte last_moon_tracking_state = 0;

  if ((lcd_state_row_0 != 0) && (lcd_state_row_1 != 0) && ((millis()-last_moon_tracking_check_time) > LCD_MOON_TRACKING_UPDATE_INTERVAL) && moon_tracking_active) {  
    update_moon_position();
    strcpy(trackingstring,"");
    strcat(trackingstring,MOON_STRING);
    dtostrf(moon_azimuth,0,LCD_DECIMAL_PLACES,temp_string);
    strcat(trackingstring,temp_string);
    if ((LCD_COLUMNS>16) && ((moon_azimuth < 100) || (abs(moon_elevation)<100))) {strcat(trackingstring,"\xDF");}
    strcat(trackingstring," ");
    dtostrf(moon_elevation,0,LCD_DECIMAL_PLACES,temp_string);
    strcat(trackingstring,temp_string);
    if ((LCD_COLUMNS>16) && ((moon_azimuth < 100) || (abs(moon_elevation)<100))) {strcat(trackingstring,"\xDF");}
    if (strlen(trackingstring) < last_strlen_moon){
      clear_display_row(LCD_MOON_OR_SUN_TRACKING_CONDITIONAL_ROW-1);
    }
    last_strlen_moon = strlen(trackingstring);
    lcd.setCursor((LCD_COLUMNS-strlen(trackingstring))/2,LCD_MOON_OR_SUN_TRACKING_CONDITIONAL_ROW-1);
    lcd.print(trackingstring);
    last_moon_tracking_check_time = millis();
    last_moon_tracking_state = 1;
  }

  if (!moon_tracking_active && last_moon_tracking_state){
    clear_display_row(LCD_MOON_OR_SUN_TRACKING_CONDITIONAL_ROW-1);
    last_moon_tracking_state = 0;
  }

  #endif //defined(OPTION_DISPLAY_MOON_TRACKING_CONTINUOUSLY) && defined(FEATURE_MOON_TRACKING)


  //  sun tracking ----------------------------------------------------------
  #if defined(FEATURE_SUN_TRACKING)

  static unsigned long last_sun_tracking_check_time = 0;
  static byte last_strlen_sun = 0;
  static byte last_sun_tracking_state = 0;

  if ((lcd_state_row_0 != 0) && (lcd_state_row_1 != 0) && ((millis()-last_sun_tracking_check_time) > LCD_SUN_TRACKING_UPDATE_INTERVAL) && sun_tracking_active) {  
    update_sun_position();
    strcpy(trackingstring,"");
    strcat(trackingstring,SUN_STRING);
    dtostrf(sun_azimuth,0,LCD_DECIMAL_PLACES,temp_string);
    strcat(trackingstring,temp_string);
    if ((LCD_COLUMNS>16) && ((sun_azimuth < 100) || (abs(sun_elevation)<100))) {strcat(trackingstring,"\xDF");}
    strcat(trackingstring," ");
    dtostrf(sun_elevation,0,LCD_DECIMAL_PLACES,temp_string);
    strcat(trackingstring,temp_string);
    if ((LCD_COLUMNS>16) && ((sun_azimuth < 100) || (abs(sun_elevation)<100))) {strcat(trackingstring,"\xDF");}
    if (strlen(trackingstring) < last_strlen_sun){
      clear_display_row(LCD_MOON_OR_SUN_TRACKING_CONDITIONAL_ROW-1);
    }
    last_strlen_sun = strlen(trackingstring);
    lcd.setCursor((LCD_COLUMNS-strlen(trackingstring))/2,LCD_MOON_OR_SUN_TRACKING_CONDITIONAL_ROW-1);
    lcd.print(trackingstring);
    last_sun_tracking_check_time = millis();
    last_sun_tracking_state = 1;
  }
  

  if (!sun_tracking_active && last_sun_tracking_state){
    clear_display_row(LCD_MOON_OR_SUN_TRACKING_CONDITIONAL_ROW-1);
    last_sun_tracking_state = 0;
  }  

  #endif //defined(FEATURE_SUN_TRACKING)

  #endif //OPTION_DISPLAY_MOON_OR_SUN_TRACKING_CONDITIONAL

  // end of moon and/or sun conditional -----

  if ((millis() - last_lcd_update) > LCD_UPDATE_TIME) {
    last_lcd_update = millis();
  }

  last_row_0_string = row_0_string;






  // clock display --- big clock ---------------------------------------------------------
  /* this is outside the normal LCD refresh timing so the seconds update as soon as possible */
  #if defined(OPTION_DISPLAY_BIG_CLOCK) && defined(FEATURE_CLOCK)
  
  char clock_big_temp_string[22] = "";
  static byte last_clock_big_seconds = 0;
  static byte last_clock_big_minutes = 0;
  static byte printed_z = 0;


  update_time();
  if ((last_clock_big_seconds != clock_seconds) && (lcd_state_row_0 != 0) && (lcd_state_row_1 != 0)){
    sprintf(clock_big_temp_string, "%s", clock_string()); 
    //clock_big_temp_string[20] 
    if (last_clock_big_minutes == clock_minutes){  // if the minutes didn't change, don't send the whole string to the LCD
      
      if ((int(float(last_clock_big_seconds)/10.0)) != (int(float(clock_seconds)/10.0))){   
        lcd.setCursor(((LCD_COLUMNS-20)/2)+17,LCD_BIG_CLOCK_ROW-1);
        lcd.print(clock_big_temp_string[17]);
      } else {
        lcd.setCursor(((LCD_COLUMNS-20)/2)+18,LCD_BIG_CLOCK_ROW-1);
      }
      lcd.print(clock_big_temp_string[18]);
      last_clock_big_seconds = clock_seconds;



    } else {  // print the whole clock
      lcd.setCursor((LCD_COLUMNS-20)/2,LCD_BIG_CLOCK_ROW-1);
      if (!printed_z){clock_big_temp_string[20] = 0;}  // I put this in to fix mysterious characters that appear at 0,0 for no reason
      lcd.print(clock_big_temp_string);
      last_clock_big_seconds = clock_seconds;
      last_clock_big_minutes = clock_minutes;
      printed_z = 1;
    }
  }
  #endif //defined(OPTION_DISPLAY_BIG_CLOCK) && defined(FEATURE_CLOCK)
  // end clock display --- big clock ---------------------------------------------------------



} /* update_display */
#endif /* ifdef FEATURE_LCD_DISPLAY */


// --------------------------------------------------------------
#ifdef FEATURE_LCD_DISPLAY
void clear_display_row(byte row_number){
  lcd.setCursor(0, row_number);
  for (byte x = 0; x < LCD_COLUMNS; x++) {
    lcd.print(" ");
  }
}
#endif

// --------------------------------------------------------------
#if defined(FEATURE_REMOTE_UNIT_SLAVE) || defined(FEATURE_YAESU_EMULATION) || defined(FEATURE_EASYCOM_EMULATION)
void get_keystroke(){
  while (control_port->available() == 0) {
  }
  while (control_port->available() > 0)
    incoming_serial_byte = control_port->read();
}
#endif // defined(FEATURE_REMOTE_UNIT_SLAVE) || defined(FEATURE_YAESU_EMULATION) || defined(FEATURE_EASYCOM_EMULATION)

// --------------------------------------------------------------
#ifdef FEATURE_YAESU_EMULATION
void print_wrote_to_memory(){

  control_port->println(F("Wrote to memory"));

}

#endif // FEATURE_YAESU_EMULATION
// --------------------------------------------------------------
#ifdef FEATURE_YAESU_EMULATION
void clear_serial_buffer(){

  delay(200);
  while (control_port->available()) incoming_serial_byte = control_port->read();
}

#endif // FEATURE_YAESU_EMULATION
// --------------------------------------------------------------


void read_settings_from_eeprom(){

  byte * p = (byte *)(void *)&configuration;
  unsigned int i;
  int ee = 0;

  for (i = 0; i < sizeof(configuration); i++) {
    *p++ = EEPROM.read(ee++);
  }

  if (configuration.magic_number == EEPROM_MAGIC_NUMBER) {
    #ifdef DEBUG_EEPROM
    if (debug_mode) {
      debug_println("read_settings_from_eeprom: reading settings from eeprom: ");
      debug_print("\nanalog_az_full_ccw");
      debug_print_int(configuration.analog_az_full_ccw);
      debug_print("\nanalog_az_full_cw");
      debug_print_int(configuration.analog_az_full_cw);
      debug_print("\nanalog_el_0_degrees");
      debug_print_int(configuration.analog_el_0_degrees);
      debug_print("\nanalog_el_max_elevation");
      debug_print_int(configuration.analog_el_max_elevation);
      debug_print("\nlast_azimuth:");
      debug_print_float(configuration.last_azimuth, 1);
      debug_print("\nlast_elevation:");
      debug_print_float(configuration.last_elevation, 1);
      debug_print("\nlast_az_incremental_encoder_position:");
      debug_print_int(configuration.last_az_incremental_encoder_position);
      debug_print("\nlast_el_incremental_encoder_position:");
      debug_print_int(configuration.last_el_incremental_encoder_position);
      debug_print("\naz_offset:");
      debug_print_float(configuration.azimuth_offset,2);
      debug_print("\nel_offset:");
      debug_print_float(configuration.elevation_offset,2);
      debug_println("");
    }
    #endif // DEBUG_EEPROM


    #if defined(FEATURE_AZ_POSITION_INCREMENTAL_ENCODER)
    az_incremental_encoder_position = configuration.last_az_incremental_encoder_position;
    #endif

    #if defined(FEATURE_ELEVATION_CONTROL) && defined(FEATURE_EL_POSITION_INCREMENTAL_ENCODER)
    el_incremental_encoder_position = configuration.last_el_incremental_encoder_position;
    #endif


    #if defined(FEATURE_AZ_POSITION_ROTARY_ENCODER)
    raw_azimuth = int(configuration.last_azimuth * HEADING_MULTIPLIER);
    if (raw_azimuth >= (360 * HEADING_MULTIPLIER)) {
      azimuth = raw_azimuth - (360 * HEADING_MULTIPLIER);
    } else {
      azimuth = raw_azimuth;
    }
      #endif // defined(FEATURE_AZ_POSITION_ROTARY_ENCODER)

      #if defined(FEATURE_ELEVATION_CONTROL) && defined(FEATURE_EL_POSITION_ROTARY_ENCODER)
    elevation = int(configuration.last_elevation * HEADING_MULTIPLIER);
      #endif // defined(FEATURE_EL_POSITION_ROTARY_ENCODER)



    #ifdef FEATURE_AZ_POSITION_PULSE_INPUT
    raw_azimuth = int(configuration.last_azimuth * HEADING_MULTIPLIER);
    if (raw_azimuth >= (360 * HEADING_MULTIPLIER)) {
      azimuth = raw_azimuth - (360 * HEADING_MULTIPLIER);
    } else {
      azimuth = raw_azimuth;
    }
    az_position_pulse_input_azimuth = configuration.last_azimuth;
    #endif // FEATURE_AZ_POSITION_PULSE_INPUT

    #if defined(FEATURE_ELEVATION_CONTROL) && defined(FEATURE_EL_POSITION_PULSE_INPUT)
    elevation = int(configuration.last_elevation * HEADING_MULTIPLIER);
    el_position_pulse_input_elevation = configuration.last_elevation;
    #endif // FEATURE_EL_POSITION_PULSE_INPUT

    #if defined(FEATURE_AZ_POSITION_PULSE_INPUT) || defined(FEATURE_AZ_POSITION_ROTARY_ENCODER)
    configuration.azimuth_offset = 0;
    #endif

    #if defined(FEATURE_EL_POSITION_PULSE_INPUT) || defined(FEATURE_EL_POSITION_ROTARY_ENCODER)
    configuration.elevation_offset = 0;
    #endif



  } else {  // initialize eeprom with default values
    #ifdef DEBUG_EEPROM
    debug_println("read_settings_from_eeprom: uninitialized eeprom, calling initialize_eeprom_with_defaults()");
    #endif // DEBUG_EEPROM
    initialize_eeprom_with_defaults();
  }
} /* read_settings_from_eeprom */
// --------------------------------------------------------------
void initialize_eeprom_with_defaults(){

  #ifdef DEBUG_EEPROM
  debug_println("initialize_eeprom_with_defaults: writing eeprom");
  #endif // DEBUG_EEPROM

  configuration.analog_az_full_ccw = ANALOG_AZ_FULL_CCW;
  configuration.analog_az_full_cw = ANALOG_AZ_FULL_CW;
  configuration.analog_el_0_degrees = ANALOG_EL_0_DEGREES;
  configuration.analog_el_max_elevation = ANALOG_EL_MAX_ELEVATION;
  configuration.last_azimuth = raw_azimuth;
  configuration.last_az_incremental_encoder_position = 0;
  configuration.last_el_incremental_encoder_position = 0;
  configuration.azimuth_offset = 0;
  configuration.elevation_offset = 0;
  #ifdef FEATURE_ELEVATION_CONTROL
  configuration.last_elevation = elevation;
  #else
  configuration.last_elevation = 0;
  #endif

  #ifdef FEATURE_STEPPER_MOTOR
  configuration.az_stepper_motor_last_direction = STEPPER_UNDEF;
  configuration.az_stepper_motor_last_pin_state = LOW;
  configuration.el_stepper_motor_last_direction = STEPPER_UNDEF;
  configuration.el_stepper_motor_last_pin_state = LOW;
  #endif //FEATURE_STEPPER_MOTOR


  write_settings_to_eeprom();

} /* initialize_eeprom_with_defaults */


// --------------------------------------------------------------
void write_settings_to_eeprom(){
  #ifdef DEBUG_EEPROM
  debug_println("write_settings_to_eeprom: writing settings to eeprom");
  #endif // DEBUG_EEPROM

  configuration.magic_number = EEPROM_MAGIC_NUMBER;

  const byte * p = (const byte *)(const void *)&configuration;
  unsigned int i;
  int ee = 0;
  for (i = 0; i < sizeof(configuration); i++) {
    EEPROM.write(ee++, *p++);
  }

  // EEPROM_writeAnything(0,configuration);
  configuration_dirty = 0;

}

// --------------------------------------------------------------

void az_check_operation_timeout(){

  // check if the last executed rotation operation has been going on too long

  if (((millis() - az_last_rotate_initiation) > OPERATION_TIMEOUT) && (az_state != IDLE)) {
    submit_request(AZ, REQUEST_KILL, 0, 78);
    #ifdef DEBUG_AZ_CHECK_OPERATION_TIMEOUT
    debug_println("az_check_operation_timeout: timeout reached, aborting rotation");
    #endif // DEBUG_AZ_CHECK_OPERATION_TIMEOUT
  }
}

// --------------------------------------------------------------

#ifdef FEATURE_TIMED_BUFFER
void clear_timed_buffer(){
  timed_buffer_status = EMPTY;
  timed_buffer_number_entries_loaded = 0;
  timed_buffer_entry_pointer = 0;
}
#endif // FEATURE_TIMED_BUFFER
// --------------------------------------------------------------
// #ifdef FEATURE_YAESU_EMULATION
// void yaesu_m_command(){

//   int parsed_azimuth = 0;

//   // parse out M command
//   if (control_port_buffer_index > 4) {  // if there are more than 4 characters in the command buffer, we got a timed interval command
//     #ifdef FEATURE_TIMED_BUFFER
//     yaesu_az_load_timed_intervals();
//     #else
//     control_port->println(F("Feature not activated ?>"));
//     #endif // FEATURE_TIMED_BUFFER
//     return;
//   } else {                         // if there are four characters, this is just a single direction setting
//     if (control_port_buffer_index == 4) {
//       parsed_azimuth = ((int(control_port_buffer[1]) - 48) * 100) + ((int(control_port_buffer[2]) - 48) * 10) + (int(control_port_buffer[3]) - 48);
//       #ifdef FEATURE_TIMED_BUFFER
//       clear_timed_buffer();
//       #endif // FEATURE_TIMED_BUFFER
//       if ((parsed_azimuth > -1) && (parsed_azimuth <= (azimuth_starting_point + azimuth_rotation_capability))) {
//         submit_request(AZ, REQUEST_AZIMUTH, (parsed_azimuth * HEADING_MULTIPLIER));
//         return;
//       }
//     }
//   }

//   control_port->println(F("?>"));

// } /* yaesu_m_command */
// #endif // FEATURE_YAESU_EMULATION
// --------------------------------------------------------------

#ifdef FEATURE_TIMED_BUFFER
void initiate_timed_buffer(byte source_port){
  if (timed_buffer_status == LOADED_AZIMUTHS) {
    timed_buffer_status = RUNNING_AZIMUTHS;
    submit_request(AZ, REQUEST_AZIMUTH, timed_buffer_azimuths[1], 79);
    last_timed_buffer_action_time = millis();
    timed_buffer_entry_pointer = 2;
    #ifdef DEBUG_TIMED_BUFFER
    debug_println("initiate_timed_buffer: changing state to RUNNING_AZIMUTHS");
    #endif // DEBUG_TIMED_BUFFER
  } else {
    #ifdef FEATURE_ELEVATION_CONTROL
    if (timed_buffer_status == LOADED_AZIMUTHS_ELEVATIONS) {
      timed_buffer_status = RUNNING_AZIMUTHS_ELEVATIONS;
      submit_request(AZ, REQUEST_AZIMUTH, timed_buffer_azimuths[1], 80);
      submit_request(EL, REQUEST_ELEVATION, timed_buffer_elevations[1], 81);
      last_timed_buffer_action_time = millis();
      timed_buffer_entry_pointer = 2;
      #ifdef DEBUG_TIMED_BUFFER
      debug_println("initiate_timed_buffer: changing state to RUNNING_AZIMUTHS_ELEVATIONS");
      #endif // DEBUG_TIMED_BUFFER
    } else {
      print_to_port(">",source_port);  // error
    }
    #endif
  }

} /* initiate_timed_buffer */
#endif // FEATURE_TIMED_BUFFER
// --------------------------------------------------------------
#ifdef FEATURE_TIMED_BUFFER
void print_timed_buffer_empty_message(){

  #ifdef DEBUG_TIMED_BUFFER
  debug_println("check_timed_interval: completed timed buffer; changing state to EMPTY");
  #endif // DEBUG_TIMED_BUFFER

}

#endif // FEATURE_TIMED_BUFFER
// --------------------------------------------------------------
#ifdef FEATURE_TIMED_BUFFER
void check_timed_interval(){

  if ((timed_buffer_status == RUNNING_AZIMUTHS) && (((millis() - last_timed_buffer_action_time) / 1000) > timed_buffer_interval_value_seconds)) {
    timed_buffer_entry_pointer++;
    #ifdef DEBUG_TIMED_BUFFER
    debug_println("check_timed_interval: executing next timed interval step - azimuths");
    #endif // DEBUG_TIMED_BUFFER
    submit_request(AZ, REQUEST_AZIMUTH, timed_buffer_azimuths[timed_buffer_entry_pointer - 1], 82);
    last_timed_buffer_action_time = millis();
    if (timed_buffer_entry_pointer == timed_buffer_number_entries_loaded) {
      clear_timed_buffer();
      print_timed_buffer_empty_message();
    }
  }
  #ifdef FEATURE_ELEVATION_CONTROL
  if ((timed_buffer_status == RUNNING_AZIMUTHS_ELEVATIONS) && (((millis() - last_timed_buffer_action_time) / 1000) > timed_buffer_interval_value_seconds)) {
    timed_buffer_entry_pointer++;
    #ifdef DEBUG_TIMED_BUFFER
    debug_println("check_timed_interval: executing next timed interval step - az and el");
    #endif // DEBUG_TIMED_BUFFER
    submit_request(AZ, REQUEST_AZIMUTH, timed_buffer_azimuths[timed_buffer_entry_pointer - 1], 83);
    submit_request(EL, REQUEST_ELEVATION, timed_buffer_elevations[timed_buffer_entry_pointer - 1], 84);
    last_timed_buffer_action_time = millis();
    if (timed_buffer_entry_pointer == timed_buffer_number_entries_loaded) {
      clear_timed_buffer();
      print_timed_buffer_empty_message();

    }
  }
  #endif
} /* check_timed_interval */
#endif // FEATURE_TIMED_BUFFER


// --------------------------------------------------------------

void read_azimuth(byte force_read){


  #ifdef FEATURE_AZ_POSITION_INCREMENTAL_ENCODER
  read_azimuth_lock = 1;
  #endif


  unsigned int previous_raw_azimuth = raw_azimuth;
  static unsigned long last_measurement_time = 0;

  #ifdef FEATURE_AZ_POSITION_INCREMENTAL_ENCODER
  static unsigned int incremental_encoder_previous_raw_azimuth = raw_azimuth;
  #endif // FEATURE_AZ_POSITION_INCREMENTAL_ENCODER

  if (heading_reading_inhibit_pin) {
    if (digitalReadEnhanced(heading_reading_inhibit_pin)) {
      return;
    }
  }

  #ifdef DEBUG_HEADING_READING_TIME
  static unsigned long last_time = 0;
  static unsigned long last_print_time = 0;
  static float average_read_time = 0;
  #endif // DEBUG_HEADING_READING_TIME

  #ifdef DEBUG_HH12
  static unsigned long last_hh12_debug = 0;
  #endif

  #ifndef FEATURE_AZ_POSITION_GET_FROM_REMOTE_UNIT
  if (((millis() - last_measurement_time) > AZIMUTH_MEASUREMENT_FREQUENCY_MS) || (force_read)) {
  #else
  if (1) {
  #endif

    #ifdef FEATURE_AZ_POSITION_POTENTIOMETER
    analog_az = analogReadEnhanced(rotator_analog_az);
    raw_azimuth = map(analog_az, configuration.analog_az_full_ccw, configuration.analog_az_full_cw, (azimuth_starting_point * HEADING_MULTIPLIER), ((azimuth_starting_point + azimuth_rotation_capability) * HEADING_MULTIPLIER));
    //raw_azimuth = map(analog_az* HEADING_MULTIPLIER, configuration.analog_az_full_ccw* HEADING_MULTIPLIER, configuration.analog_az_full_cw* HEADING_MULTIPLIER, (azimuth_starting_point * HEADING_MULTIPLIER), ((azimuth_starting_point + azimuth_rotation_capability) * HEADING_MULTIPLIER));

    #ifdef FEATURE_AZIMUTH_CORRECTION
    raw_azimuth = (correct_azimuth(raw_azimuth / (float) HEADING_MULTIPLIER) * HEADING_MULTIPLIER);
    #endif // FEATURE_AZIMUTH_CORRECTION
    raw_azimuth = raw_azimuth + (configuration.azimuth_offset * HEADING_MULTIPLIER);
    if (AZIMUTH_SMOOTHING_FACTOR > 0) {
      raw_azimuth = (raw_azimuth * (1 - (AZIMUTH_SMOOTHING_FACTOR / 100.))) + (previous_raw_azimuth * (AZIMUTH_SMOOTHING_FACTOR / 100.));
    }
    if (raw_azimuth >= (360 * HEADING_MULTIPLIER)) {
      azimuth = raw_azimuth - (360 * HEADING_MULTIPLIER);
      if (azimuth >= (360 * HEADING_MULTIPLIER)) {
        azimuth = azimuth - (360 * HEADING_MULTIPLIER);
      }
    } else {
      if (raw_azimuth < 0) {
        azimuth = raw_azimuth + (360 * HEADING_MULTIPLIER);
      } else {
        azimuth = raw_azimuth;
      }
    }
    #endif // FEATURE_AZ_POSITION_POTENTIOMETER

    #ifdef FEATURE_AZ_POSITION_GET_FROM_REMOTE_UNIT
    #if defined(FEATURE_MASTER_WITH_SERIAL_SLAVE) || defined(FEATURE_MASTER_WITH_ETHERNET_SLAVE)
    static unsigned long last_remote_unit_az_query_time = 0;
    // do we have a command result waiting for us?
    if (remote_unit_command_results_available == REMOTE_UNIT_AZ_COMMAND) {

      #ifdef DEBUG_HEADING_READING_TIME
      average_read_time = (average_read_time + (millis() - last_time)) / 2.0;
      last_time = millis();

      if (debug_mode) {
        if ((millis() - last_print_time) > 1000) {
          debug_println("read_azimuth: avg read frequency: ");
          debug_print_float(average_read_time, 2);
          debug_println("");
          last_print_time = millis();
        }
      }
      #endif // DEBUG_HEADING_READING_TIME
      raw_azimuth = remote_unit_command_result_float * HEADING_MULTIPLIER;


      #ifdef FEATURE_AZIMUTH_CORRECTION
      raw_azimuth = (correct_azimuth(raw_azimuth / (float) HEADING_MULTIPLIER) * HEADING_MULTIPLIER);
      #endif // FEATURE_AZIMUTH_CORRECTION

      raw_azimuth = raw_azimuth + (configuration.azimuth_offset * HEADING_MULTIPLIER);

      if (AZIMUTH_SMOOTHING_FACTOR > 0) {
        raw_azimuth = (raw_azimuth * (1 - (AZIMUTH_SMOOTHING_FACTOR / 100))) + (previous_raw_azimuth * (AZIMUTH_SMOOTHING_FACTOR / 100));
      }
      if (raw_azimuth >= (360 * HEADING_MULTIPLIER)) {
        azimuth = raw_azimuth - (360 * HEADING_MULTIPLIER);
        if (azimuth >= (360 * HEADING_MULTIPLIER)) {
          azimuth = azimuth - (360 * HEADING_MULTIPLIER);
        }
      } else {
        if (raw_azimuth < 0) {
          azimuth = raw_azimuth + (360 * HEADING_MULTIPLIER);
        } else {
          azimuth = raw_azimuth;
        }
      }
      remote_unit_command_results_available = 0;
    } else {
      // is it time to request the azimuth?
      if ((millis() - last_remote_unit_az_query_time) > AZ_REMOTE_UNIT_QUERY_TIME_MS) {
        if (submit_remote_command(REMOTE_UNIT_AZ_COMMAND, 0, 0)) {
          last_remote_unit_az_query_time = millis();
        }
      }
    }
    #endif // defined(FEATURE_MASTER_WITH_SERIAL_SLAVE) || defined(FEATURE_MASTER_WITH_ETHERNET_SLAVE)
    #endif // FEATURE_AZ_POSITION_GET_FROM_REMOTE_UNIT



    #ifdef FEATURE_AZ_POSITION_ROTARY_ENCODER
    static byte az_position_encoder_state = 0;
    az_position_encoder_state = ttable[az_position_encoder_state & 0xf][((digitalReadEnhanced(az_rotary_position_pin2) << 1) | digitalReadEnhanced(az_rotary_position_pin1))];
    byte az_position_encoder_result = az_position_encoder_state & 0x30;
    if (az_position_encoder_result) {
      if (az_position_encoder_result == DIR_CW) {
        configuration.last_azimuth = configuration.last_azimuth + AZ_POSITION_ROTARY_ENCODER_DEG_PER_PULSE;
        #ifdef DEBUG_POSITION_ROTARY_ENCODER
        debug_println("read_azimuth: AZ_POSITION_ROTARY_ENCODER: CW");
        #endif // DEBUG_POSITION_ROTARY_ENCODER
      }
      if (az_position_encoder_result == DIR_CCW) {
        configuration.last_azimuth = configuration.last_azimuth - AZ_POSITION_ROTARY_ENCODER_DEG_PER_PULSE;
        #ifdef DEBUG_POSITION_ROTARY_ENCODER
        debug_println("read_azimuth: AZ_POSITION_ROTARY_ENCODER: CCW");
        #endif // DEBUG_POSITION_ROTARY_ENCODER
      }

      #ifdef OPTION_AZ_POSITION_ROTARY_ENCODER_HARD_LIMIT
      if (configuration.last_azimuth < azimuth_starting_point) {
        configuration.last_azimuth = azimuth_starting_point;
      }
      if (configuration.last_azimuth > (azimuth_starting_point + azimuth_rotation_capability)) {
        configuration.last_azimuth = (azimuth_starting_point + azimuth_rotation_capability);
      }
      #else
      if (configuration.last_azimuth < 0) {
        configuration.last_azimuth += 360;
      }
      if (configuration.last_azimuth >= 360) {
        configuration.last_azimuth -= 360;
      }
      #endif // OPTION_AZ_POSITION_ROTARY_ENCODER_HARD_LIMIT


      raw_azimuth = int(configuration.last_azimuth * HEADING_MULTIPLIER);


      #ifdef FEATURE_AZIMUTH_CORRECTION
      raw_azimuth = (correct_azimuth(raw_azimuth / (float) HEADING_MULTIPLIER) * HEADING_MULTIPLIER);
      #endif // FEATURE_AZIMUTH_CORRECTION

      if (raw_azimuth >= (360 * HEADING_MULTIPLIER)) {
        azimuth = raw_azimuth - (360 * HEADING_MULTIPLIER);
      } else {
        azimuth = raw_azimuth;
      }
      configuration_dirty = 1;
    }
    #endif // FEATURE_AZ_POSITION_ROTARY_ENCODER

    #ifdef FEATURE_AZ_POSITION_HMC5883L
    MagnetometerScaled scaled = compass.ReadScaledAxis(); // scaled values from compass.

    #ifdef DEBUG_HMC5883L
    debug_print("read_azimuth: HMC5883L x:");
    debug_print_float(scaled.XAxis,4);
    debug_print(" y:");
    debug_print_float(scaled.YAxis,4);
    debug_println("");
    #endif //DEBUG_HMC5883L


    float heading = atan2(scaled.YAxis, scaled.XAxis);
    //  heading += declinationAngle;
    // Correct for when signs are reversed.
    if (heading < 0) heading += 2 * PI;
    if (heading > 2 * PI) heading -= 2 * PI;
    raw_azimuth = (heading * RAD_TO_DEG) * HEADING_MULTIPLIER; // radians to degree
    if (AZIMUTH_SMOOTHING_FACTOR > 0) {
      raw_azimuth = (raw_azimuth * (1 - (AZIMUTH_SMOOTHING_FACTOR / 100))) + (previous_raw_azimuth * (AZIMUTH_SMOOTHING_FACTOR / 100));
    }
    #ifdef FEATURE_AZIMUTH_CORRECTION
    raw_azimuth = (correct_azimuth(raw_azimuth / (float) HEADING_MULTIPLIER) * HEADING_MULTIPLIER);
    #endif // FEATURE_AZIMUTH_CORRECTION
    raw_azimuth = raw_azimuth + (configuration.azimuth_offset * HEADING_MULTIPLIER);
    azimuth = raw_azimuth;
    #endif // FEATURE_AZ_POSITION_HMC5883L





    #ifdef FEATURE_AZ_POSITION_ADAFRUIT_LSM303
    lsm.read();
    float heading = atan2(lsm.magData.y, lsm.magData.x);
    //  heading += declinationAngle;
    // Correct for when signs are reversed.
    if (heading < 0) heading += 2 * PI;
    if (heading > 2 * PI) heading -= 2 * PI;
    raw_azimuth = (heading * RAD_TO_DEG) * HEADING_MULTIPLIER; // radians to degree
    #ifdef FEATURE_AZIMUTH_CORRECTION
    raw_azimuth = (correct_azimuth(raw_azimuth / (float) HEADING_MULTIPLIER) * HEADING_MULTIPLIER);
    #endif // FEATURE_AZIMUTH_CORRECTION
    raw_azimuth = raw_azimuth + (configuration.azimuth_offset * HEADING_MULTIPLIER);
    if (AZIMUTH_SMOOTHING_FACTOR > 0) {
      raw_azimuth = (raw_azimuth * (1 - (AZIMUTH_SMOOTHING_FACTOR / 100))) + (previous_raw_azimuth * (AZIMUTH_SMOOTHING_FACTOR / 100));
    }
    azimuth = raw_azimuth;
    #endif // FEATURE_AZ_POSITION_ADAFRUIT_LSM303


    #ifdef FEATURE_AZ_POSITION_POLOLU_LSM303
    compass.read();   
    #ifdef DEBUG_POLOLU_LSM303_CALIBRATION
    running_min.x = min(running_min.x, compass.m.x);
    running_min.y = min(running_min.y, compass.m.y);
    running_min.z = min(running_min.z, compass.m.z);
    running_max.x = max(running_max.x, compass.m.x);
    running_max.y = max(running_max.y, compass.m.y);
    running_max.z = max(running_max.z, compass.m.z);
    snprintf(report, sizeof(report), "min: {%+6d, %+6d, %+6d}    max: {%+6d, %+6d, %+6d}",
    running_min.x, running_min.y, running_min.z,
    running_max.x, running_max.y, running_max.z);
    Serial.println(report);
    #endif // DEBUG_POLOLU_LSM303_CALIBRATION
    //lsm.read();
        
    /*
    When given no arguments, the heading() function returns the angular
    difference in the horizontal plane between a default vector and
    north, in degrees.
  
    The default vector is chosen by the library to point along the
    surface of the PCB, in the direction of the top of the text on the
    silkscreen. This is the +X axis on the Pololu LSM303D carrier and
    the -Y axis on the Pololu LSM303DLHC, LSM303DLM, and LSM303DLH
    carriers.
    
    To use a different vector as a reference, use the version of heading()
    that takes a vector argument; for example, use
  
    compass.heading((LSM303::vector<int>){0, 0, 1});
  
    to use the +Z axis as a reference.
    */
    float heading = compass.heading();
    
    //float heading = atan2(lsm.magData.y, lsm.magData.x);
    //  heading += declinationAngle; 
    // Correct for when signs are reversed.
    /*
    if (heading < 0) heading += 2 * PI;
    if (heading > 2 * PI) heading -= 2 * PI;
    raw_azimuth = (heading * RAD_TO_DEG) * HEADING_MULTIPLIER; // radians to degree
    */
    raw_azimuth = heading * HEADING_MULTIPLIER ;  // pololu library returns float value of actual heading.
    #ifdef FEATURE_AZIMUTH_CORRECTION
    raw_azimuth = (correct_azimuth(raw_azimuth / (float) HEADING_MULTIPLIER) * HEADING_MULTIPLIER);
    #endif // FEATURE_AZIMUTH_CORRECTION
    raw_azimuth = raw_azimuth + (configuration.azimuth_offset * HEADING_MULTIPLIER);
    if (AZIMUTH_SMOOTHING_FACTOR > 0) {
      raw_azimuth = (raw_azimuth * (1 - (AZIMUTH_SMOOTHING_FACTOR / 100))) + (previous_raw_azimuth * (AZIMUTH_SMOOTHING_FACTOR / 100));
    }
    azimuth = raw_azimuth;
    #endif // FEATURE_AZ_POSITION_POLOLU_LSM303



    #ifdef FEATURE_AZ_POSITION_PULSE_INPUT
    #ifdef DEBUG_POSITION_PULSE_INPUT
//    if (az_position_pule_interrupt_handler_flag) {
//      control_port->print(F("read_azimuth: az_position_pusle_interrupt_handler_flag: "));
//      control_port->println(az_position_pule_interrupt_handler_flag);
//      az_position_pule_interrupt_handler_flag = 0;
//    }
    #endif // DEBUG_POSITION_PULSE_INPUT
    static float last_az_position_pulse_input_azimuth = az_position_pulse_input_azimuth;
    if (az_position_pulse_input_azimuth != last_az_position_pulse_input_azimuth) {
      #ifdef DEBUG_POSITION_PULSE_INPUT
//        if (debug_mode){
//          control_port->print(F("read_azimuth: last_az_position_pulse_input_azimuth:"));
//          control_port->print(last_az_position_pulse_input_azimuth);
//          control_port->print(F(" az_position_pulse_input_azimuth:"));
//          control_port->print(az_position_pulse_input_azimuth);
//          control_port->print(F(" az_pulse_counter:"));
//          control_port->println(az_pulse_counter);
//        }
      #endif // DEBUG_POSITION_PULSE_INPUT
      configuration.last_azimuth = az_position_pulse_input_azimuth;
      configuration_dirty = 1;
      last_az_position_pulse_input_azimuth = az_position_pulse_input_azimuth;
      raw_azimuth = int(configuration.last_azimuth * HEADING_MULTIPLIER);
      #ifdef FEATURE_AZIMUTH_CORRECTION
      raw_azimuth = (correct_azimuth(raw_azimuth / (float) HEADING_MULTIPLIER) * HEADING_MULTIPLIER);
      #endif // FEATURE_AZIMUTH_CORRECTION
      raw_azimuth = raw_azimuth + (configuration.azimuth_offset * HEADING_MULTIPLIER);
      if (raw_azimuth >= (360 * HEADING_MULTIPLIER)) {
        azimuth = raw_azimuth - (360 * HEADING_MULTIPLIER);
      } else {
        azimuth = raw_azimuth;
      }
    }
    #endif // FEATURE_AZ_POSITION_PULSE_INPUT

    #ifdef FEATURE_AZ_POSITION_HH12_AS5045_SSI
    raw_azimuth = int(azimuth_hh12.heading() * HEADING_MULTIPLIER);
    #ifdef DEBUG_HH12
    if ((millis() - last_hh12_debug) > 5000) {
      control_port->print(F("read_azimuth: HH-12 raw: "));
      control_port->println(raw_azimuth);
      last_hh12_debug = millis();
    }
    #endif // DEBUG_HH12
    #ifdef FEATURE_AZIMUTH_CORRECTION
    raw_azimuth = (correct_azimuth(raw_azimuth / (float) HEADING_MULTIPLIER) * HEADING_MULTIPLIER);
    #endif // FEATURE_AZIMUTH_CORRECTION
    raw_azimuth = raw_azimuth + (configuration.azimuth_offset * HEADING_MULTIPLIER);
    if (raw_azimuth >= (360 * HEADING_MULTIPLIER)) {
      azimuth = raw_azimuth - (360 * HEADING_MULTIPLIER);
    } else {
      if (raw_azimuth < 0) {
        azimuth = raw_azimuth + (360 * HEADING_MULTIPLIER);
      } else {
        azimuth = raw_azimuth;
      }
    }
    #endif // FEATURE_AZ_POSITION_HH12_AS5045_SSI

    #ifdef FEATURE_AZ_POSITION_INCREMENTAL_ENCODER
    if (AZIMUTH_STARTING_POINT_DEFAULT == 0) {
      raw_azimuth = (((((az_incremental_encoder_position) / (AZ_POSITION_INCREMENTAL_ENCODER_PULSES_PER_REV*4.)) * 360.0)) * HEADING_MULTIPLIER);
    } else {
      if (az_incremental_encoder_position > (AZ_POSITION_INCREMENTAL_ENCODER_PULSES_PER_REV*4.)) {
        raw_azimuth = (((((az_incremental_encoder_position - (AZ_POSITION_INCREMENTAL_ENCODER_PULSES_PER_REV*4.)) / (AZ_POSITION_INCREMENTAL_ENCODER_PULSES_PER_REV*4.)) * 360.0)) * HEADING_MULTIPLIER);
      } else {
        raw_azimuth = (((((az_incremental_encoder_position + (AZ_POSITION_INCREMENTAL_ENCODER_PULSES_PER_REV*4.)) / (AZ_POSITION_INCREMENTAL_ENCODER_PULSES_PER_REV*4.)) * 360.0)) * HEADING_MULTIPLIER);
      }
    }
    #ifdef FEATURE_AZIMUTH_CORRECTION
    raw_azimuth = (correct_azimuth(raw_azimuth / (float) HEADING_MULTIPLIER) * HEADING_MULTIPLIER);
    #endif // FEATURE_AZIMUTH_CORRECTION
    raw_azimuth = raw_azimuth + (configuration.azimuth_offset * HEADING_MULTIPLIER);
    if (raw_azimuth >= (360 * HEADING_MULTIPLIER)) {
      azimuth = raw_azimuth - (360 * HEADING_MULTIPLIER);
    } else {
      azimuth = raw_azimuth;
    }
    if (raw_azimuth != incremental_encoder_previous_raw_azimuth) {
      configuration.last_az_incremental_encoder_position = az_incremental_encoder_position;
      configuration_dirty = 1;
      incremental_encoder_previous_raw_azimuth = raw_azimuth;
    }
    #endif // FEATURE_AZ_POSITION_INCREMENTAL_ENCODER




    last_measurement_time = millis();
  }

  #ifdef FEATURE_AZ_POSITION_INCREMENTAL_ENCODER
  read_azimuth_lock = 0;
  #endif



} /* read_azimuth */

// --------------------------------------------------------------

void output_debug(){

  #ifdef DEBUG_DUMP

  char tempstring[32] = "";

  #if defined(FEATURE_REMOTE_UNIT_SLAVE) || defined(FEATURE_YAESU_EMULATION) || defined(FEATURE_EASYCOM_EMULATION)

  if (((millis() - last_debug_output_time) >= 3000) && (debug_mode)) {

    #ifdef DEBUG_GPS_SERIAL
    debug_println("");
    #endif //DEBUG_GPS_SERIAL

    //port_flush();

    debug_print("debug: \t");
    debug_print(CODE_VERSION);
    #ifdef HARDWARE_WB6KCN
    debug_print(" HARDWARE_WB6KCN");
    #endif
    #ifdef HARDWARE_M0UPU
    debug_print(" HARDWARE_M0UPU");
    #endif    
    #ifdef HARDWARE_EA4TX_ARS_USB
    debug_print(" HARDWARE_EA4TX_ARS_USB");
    #endif      
    debug_print("\t\t");

    #ifdef FEATURE_CLOCK
    update_time();
    sprintf(tempstring, "%s", clock_string());
    debug_print(tempstring);
    #else // FEATURE_CLOCK
    dtostrf((millis() / 1000),0,0,tempstring);
    debug_print(tempstring);
    #endif // FEATURE_CLOCK

    #if defined(FEATURE_GPS) || defined(FEATURE_RTC) || (defined(FEATURE_CLOCK) && defined(OPTION_SYNC_MASTER_CLOCK_TO_SLAVE))
    debug_print("\t");
    debug_print(clock_status_string());
    #endif // defined(FEATURE_GPS) || defined(FEATURE_RTC) || (defined(FEATURE_CLOCK) && defined(OPTION_SYNC_MASTER_CLOCK_TO_SLAVE))

    #if defined(FEATURE_MOON_TRACKING) || defined(FEATURE_SUN_TRACKING)
    debug_print("\t");
    sprintf(tempstring, "%s", coordinate_string());
    debug_print(tempstring); 
    debug_print(" ");
    debug_print(coordinates_to_maidenhead(latitude,longitude));
    #endif

    debug_print("\t\t");
    #ifdef DEBUG_MEMORY
    void * HP = malloc(4);
    if (HP) {free(HP);}
    unsigned long free = (unsigned long)SP - (unsigned long)HP;
    sprintf(tempstring,"%lu",(unsigned long)free);
    debug_print(tempstring);
    debug_print("b free");
    #endif //DEBUG_MEMORY

    #ifdef FEATURE_YAESU_EMULATION
    debug_print("\t\tGS-232");
    #ifdef OPTION_GS_232B_EMULATION
    debug_print("B");
    #else
    debug_print("A");
    #endif
    #endif // FEATURE_YAESU_EMULATION

    #ifdef FEATURE_PARK
    switch (park_status) {
      case NOT_PARKED: debug_print("\tNOT_PARKED"); break;
      case PARK_INITIATED: debug_print("\tPARK_INITIATED"); break;
      case PARKED: debug_print("\tPARKED"); break;
    }
    #endif // FEATURE_PARK


    debug_print("\n");

    debug_print("\tAZ: ");
    switch (az_state) {
      case IDLE: debug_print("IDLE"); break;
      #ifndef HARDWARE_EA4TX_ARS_USB
      case SLOW_START_CW: debug_print("SLOW_START_CW"); break;
      case SLOW_START_CCW: debug_print("SLOW_START_CCW"); break;
      #endif //ifndef HARDWARE_EA4TX_ARS_USB
      case NORMAL_CW: debug_print("NORMAL_CW"); break;
      case NORMAL_CCW: debug_print("NORMAL_CCW"); break;
      #ifndef HARDWARE_EA4TX_ARS_USB
      case SLOW_DOWN_CW: debug_print("SLOW_DOWN_CW"); break;
      case SLOW_DOWN_CCW: debug_print("SLOW_DOWN_CCW"); break;
      case INITIALIZE_SLOW_START_CW: debug_print("INITIALIZE_SLOW_START_CW"); break;
      case INITIALIZE_SLOW_START_CCW: debug_print("INITIALIZE_SLOW_START_CCW"); break;
      case INITIALIZE_TIMED_SLOW_DOWN_CW: debug_print("INITIALIZE_TIMED_SLOW_DOWN_CW"); break;
      case INITIALIZE_TIMED_SLOW_DOWN_CCW: debug_print("INITIALIZE_TIMED_SLOW_DOWN_CCW"); break;
      case TIMED_SLOW_DOWN_CW: debug_print("TIMED_SLOW_DOWN_CW"); break;
      case TIMED_SLOW_DOWN_CCW: debug_print("TIMED_SLOW_DOWN_CCW"); break;
      case INITIALIZE_DIR_CHANGE_TO_CW: debug_print("INITIALIZE_DIR_CHANGE_TO_CW"); break;
      case INITIALIZE_DIR_CHANGE_TO_CCW: debug_print("INITIALIZE_DIR_CHANGE_TO_CCW"); break;
      case INITIALIZE_NORMAL_CW: debug_print("INITIALIZE_NORMAL_CW"); break;
      case INITIALIZE_NORMAL_CCW: debug_print("INITIALIZE_NORMAL_CCW"); break; 
      #endif //ifndef HARDWARE_EA4TX_ARS_USB     
    }

    debug_print("\tQ: ");
    switch (az_request_queue_state) {
      case NONE: debug_print("-"); break;
      case IN_QUEUE: debug_print("IN_QUEUE"); break;
      case IN_PROGRESS_TIMED: debug_print("IN_PROGRESS_TIMED"); break;
      case IN_PROGRESS_TO_TARGET: debug_print("IN_PROGRESS_TO_TARGET"); break;
    }

    debug_print(" AZ: ");
    debug_print_float((azimuth / LCD_HEADING_MULTIPLIER), LCD_DECIMAL_PLACES);
    debug_print(" (raw: ");
    debug_print_float((raw_azimuth / LCD_HEADING_MULTIPLIER), LCD_DECIMAL_PLACES);
    debug_print(")");


    if (az_state != IDLE) {
      debug_print("  Target: ");
      debug_print_float((target_azimuth / LCD_HEADING_MULTIPLIER), LCD_DECIMAL_PLACES);
   

      debug_print(" (raw: ");

      debug_print_float((target_raw_azimuth / LCD_HEADING_MULTIPLIER), LCD_DECIMAL_PLACES);
      debug_print(")");

      debug_print("  Secs_left: ");
      debug_print_int((OPERATION_TIMEOUT - (millis() - az_last_rotate_initiation)) / 1000);
    }

    #ifdef FEATURE_AZ_POSITION_POTENTIOMETER
    debug_print("  Analog: ");
    dtostrf(analog_az,0,0,tempstring);
    debug_print(tempstring);
    debug_print(" (");
    dtostrf(configuration.analog_az_full_ccw,0,0,tempstring);
    debug_print(tempstring);
    debug_print("-");
    dtostrf(configuration.analog_az_full_cw,0,0,tempstring);
    debug_print(tempstring);
    debug_print(") ");
    #endif // FEATURE_AZ_POSITION_POTENTIOMETER
//added Craig Benson UNSW@ADFA 2015-05-07
//debug_println("Craig \n");
//end added 2015-05-07
    debug_print("\t[");
    debug_print_int(azimuth_starting_point);
    debug_print("+");
    debug_print_int(azimuth_rotation_capability);
    debug_print("]");

    #ifndef HARDWARE_EA4TX_ARS_USB
    debug_print("  AZ Speed Norm: ");
//added Craig Benson UNSW@ADFA 2015-05-07
//debug_println("Craig - needs following delay to make loop work\n");
//delay(500);
//end added 2015-05-07
debug_print_int(normal_az_speed_voltage);

    debug_print(" Current: ");
    debug_print_int(current_az_speed_voltage);


    if (az_speed_pot) {
      debug_print("  AZ Speed Pot: ");
      debug_print_int(analogReadEnhanced(az_speed_pot));
    }
    if (az_preset_pot) {
      debug_print(" AZ Preset Pot Analog: ");
      debug_print_int(analogReadEnhanced(az_preset_pot));
      debug_print("  AZ Preset Pot Setting: ");
      dtostrf((map(analogReadEnhanced(az_preset_pot), AZ_PRESET_POT_FULL_CW, AZ_PRESET_POT_FULL_CCW, AZ_PRESET_POT_FULL_CW_MAP, AZ_PRESET_POT_FULL_CCW_MAP)),0,0,tempstring);
      debug_print(tempstring);
    }

    debug_print("\tOffset: ");
    dtostrf(configuration.azimuth_offset,0,2,tempstring);
    debug_print(tempstring);
    #endif // ndef HARDWARE_EA4TX_ARS_USB
    debug_println("");


    #ifdef FEATURE_ELEVATION_CONTROL
    debug_print("\tEL: ");
    switch (el_state) {
      case IDLE: debug_print("IDLE"); break;
      #ifndef HARDWARE_EA4TX_ARS_USB
      case SLOW_START_UP: debug_print("SLOW_START_UP"); break;
      case SLOW_START_DOWN: debug_print("SLOW_START_DOWN"); break;
      #endif //ifndef HARDWARE_EA4TX_ARS_USB
      case NORMAL_UP: debug_print("NORMAL_UP"); break;
      case NORMAL_DOWN: debug_print("NORMAL_DOWN"); break;
      #ifndef HARDWARE_EA4TX_ARS_USB
      case SLOW_DOWN_DOWN: debug_print("SLOW_DOWN_DOWN"); break;
      case SLOW_DOWN_UP: debug_print("SLOW_DOWN_UP"); break;
      case TIMED_SLOW_DOWN_UP: debug_print("TIMED_SLOW_DOWN_UP"); break;
      case TIMED_SLOW_DOWN_DOWN: debug_print("TIMED_SLOW_DOWN_DOWN"); break;
      #endif //ifndef HARDWARE_EA4TX_ARS_USB
    }

    debug_print("\tQ: ");
    switch (el_request_queue_state) {
      case NONE: debug_print("-"); break;
      case IN_QUEUE: debug_print("IN_QUEUE"); break;
      case IN_PROGRESS_TIMED: debug_print("IN_PROGRESS_TIMED"); break;
      case IN_PROGRESS_TO_TARGET: debug_print("IN_PROGRESS_TO_TARGET"); break;
    }
    debug_print(" EL: ");
    dtostrf(elevation / LCD_HEADING_MULTIPLIER, 0, LCD_DECIMAL_PLACES,tempstring);
    debug_print(tempstring);
    if (el_state != IDLE) {
      debug_print("\tTarget: ");
      dtostrf(target_elevation / LCD_HEADING_MULTIPLIER, 0, LCD_DECIMAL_PLACES,tempstring);
      debug_print(tempstring);
    }

    #ifdef FEATURE_EL_POSITION_POTENTIOMETER
    debug_print("\tEL Analog: ");
    dtostrf(analog_el,0,0,tempstring);
    debug_print(tempstring);
    debug_print(" (");
    dtostrf(configuration.analog_el_0_degrees,0,0,tempstring);
    debug_print(tempstring);
    debug_print("-");
    dtostrf(configuration.analog_el_max_elevation,0,0,tempstring);
    debug_print(tempstring);
    debug_print(") ");
    #endif // FEATURE_EL_POSITION_POTENTIOMETER
   
    #ifndef HARDWARE_EA4TX_ARS_USB
    debug_print("  EL Speed Norm: ");
    debug_print_int(normal_el_speed_voltage);


    debug_print(" Current: ");
    debug_print_int(current_el_speed_voltage);

    debug_print("\tOffset: ");
    debug_print_float(configuration.elevation_offset, 2);
    #endif //ifndef HARDWARE_EA4TX_ARS_USB
    debug_println("");
    #endif // FEATURE_ELEVATION_CONTROL

    //port_flush();

    #ifdef FEATURE_TIMED_BUFFER
    if (timed_buffer_status != EMPTY) {
      debug_print("\tTimed interval buff: ");
      switch (timed_buffer_status) {
        // case EMPTY: debug_print("EMPTY"); break;
        case LOADED_AZIMUTHS: debug_print("LOADED_AZIMUTHS"); break;
        case RUNNING_AZIMUTHS: debug_print("RUNNING_AZIMUTHS"); break;
        #ifdef FEATURE_ELEVATION_CONTROL
        case LOADED_AZIMUTHS_ELEVATIONS: debug_print("LOADED_AZIMUTHS_ELEVATIONS"); break;
        case RUNNING_AZIMUTHS_ELEVATIONS: debug_print("RUNNING_AZIMUTHS_ELEVATIONS"); break;
        #endif
      }

      debug_print("\tInterval (secs): ");
      debug_print_int(timed_buffer_interval_value_seconds);
      debug_print("\tEntries: ");
      debug_print_int(timed_buffer_number_entries_loaded);
      debug_print("\tEntry ptr: ");
      debug_print_int(timed_buffer_entry_pointer);
      debug_print("\tSecs since last action: ");
      debug_print_int((millis() - last_timed_buffer_action_time) / 1000);

      if (timed_buffer_number_entries_loaded > 0) {
        for (int x = 0; x < timed_buffer_number_entries_loaded; x++) {
          debug_print_int(x + 1);
          debug_print("\t:");
          debug_print_int(timed_buffer_azimuths[x] / HEADING_MULTIPLIER);
        #ifdef FEATURE_ELEVATION_CONTROL
          debug_print("\t- ");
          debug_print_int(timed_buffer_elevations[x] / HEADING_MULTIPLIER);
        #endif
          debug_print("\n");
        }
        debug_println("");
      }

    } // if (timed_buffer_status != EMPTY)
    #endif // FEATURE_TIMED_BUFFER
//added Craig Benson UNSW@ADFA 2015-05-07
//debug_println("Craig up here\n");
//end added 2015-05-07

    #if defined(FEATURE_MASTER_WITH_SERIAL_SLAVE) || defined(FEATURE_MASTER_WITH_ETHERNET_SLAVE)
    /*debug_print("\tRemote Slave: Command: ");
    debug_print_int(remote_unit_command_submitted);*/
    debug_print("\tRemote Slave: Good: ");
    debug_print_float(remote_unit_good_results,0);
    debug_print(" Bad: ");
    debug_print_int(remote_unit_bad_results);
    /*debug_print(" Index: ");
    debug_print_int(remote_unit_port_buffer_index);*/
    debug_print(" CmdTouts: ");
    debug_print_int(remote_unit_command_timeouts);
    debug_print(" BuffTouts: ");
    debug_print_int(remote_unit_incoming_buffer_timeouts);
    /*debug_print(" Result: ");
    debug_print_float(remote_unit_command_result_float,2);*/
    debug_println("");
    #endif // defined(FEATURE_MASTER_WITH_SERIAL_SLAVE) || defined(FEATURE_MASTER_WITH_ETHERNET_SLAVE)

    #if defined(FEATURE_MASTER_WITH_ETHERNET_SLAVE)
    debug_print("\tEthernet Slave TCP Link State: ");
    switch(ethernetslavelinkclient0_state){
      case ETHERNET_SLAVE_DISCONNECTED: debug_print("DIS");
      case ETHERNET_SLAVE_CONNECTED: debug_print("CONNECTED");
    }
    debug_print(" Reconnects: ");
    debug_print_int(ethernet_slave_reconnects);  
    debug_println("");
    #endif // defined(FEATURE_MASTER_WITH_ETHERNET_SLAVE)  

    #ifdef DEBUG_POSITION_PULSE_INPUT
    static unsigned long last_pulse_count_time = 0;
    static unsigned long last_az_pulse_counter = 0;
    static unsigned long last_el_pulse_counter = 0;
    debug_print("\tPulse counters: AZ: ");
    debug_print_int(az_pulse_counter);
    debug_print(" AZ Ambiguous: ");
    debug_print_int(az_pulse_counter_ambiguous);
    debug_print(" EL: ");
    debug_print_int(el_pulse_counter);
    debug_print(" EL Ambiguous: ");
    debug_print_int(el_pulse_counter_ambiguous);
    debug_print(" Rate per sec: AZ: ");
    debug_print_float(((az_pulse_counter - last_az_pulse_counter) / ((millis() - last_pulse_count_time) / 1000.0)),2);
    debug_print(" EL: ");
    debug_print_float(((el_pulse_counter - last_el_pulse_counter) / ((millis() - last_pulse_count_time) / 1000.0)),2);
    debug_println("");
    last_az_pulse_counter = az_pulse_counter;
    last_el_pulse_counter = el_pulse_counter;
    last_pulse_count_time = millis();
    #endif // DEBUG_POSITION_PULSE_INPUT


    #if defined(FEATURE_AZ_POSITION_INCREMENTAL_ENCODER) && defined(DEBUG_AZ_POSITION_INCREMENTAL_ENCODER)
    debug_print("\taz_position_incremental_encoder_interrupt: ");
    debug_print_int(az_position_incremental_encoder_interrupt);
    debug_print("\taz_incremental_encoder_position: ");
    debug_print_int(az_incremental_encoder_position);
    #endif // DEBUG_AZ_POSITION_INCREMENTAL_ENCODER
    #if defined(FEATURE_EL_POSITION_INCREMENTAL_ENCODER) && defined(DEBUG_EL_POSITION_INCREMENTAL_ENCODER)
    debug_print("\n\tel_position_incremental_encoder_interrupt: ");
    debug_print_int(el_position_incremental_encoder_interrupt);
    debug_print("\tel_incremental_encoder_position: ");
    debug_print_int(el_incremental_encoder_position);
    #endif // DEBUG_EL_POSITION_INCREMENTAL_ENCODER
    #if (defined(FEATURE_AZ_POSITION_INCREMENTAL_ENCODER) && defined(DEBUG_AZ_POSITION_INCREMENTAL_ENCODER)) || (defined(FEATURE_EL_POSITION_INCREMENTAL_ENCODER) && defined(DEBUG_EL_POSITION_INCREMENTAL_ENCODER))
    debug_println("");
    #endif




    #ifdef FEATURE_MOON_TRACKING
    update_moon_position();
    debug_print(moon_status_string());
    #endif // FEATURE_MOON_TRACKING

    #ifdef FEATURE_SUN_TRACKING
    update_sun_position();
    debug_print(sun_status_string());
    #endif // FEATURE_SUN_TRACKING

    #if defined(FEATURE_MOON_TRACKING) || defined(FEATURE_SUN_TRACKING)
    debug_println("");
    #endif //defined(FEATURE_MOON_TRACKING) || defined(FEATURE_SUN_TRACKING)

    #ifdef FEATURE_GPS
    unsigned long gps_chars = 0;
    unsigned short gps_good_sentences = 0;
    unsigned short gps_failed_checksum = 0;
    char gps_temp_string[12] = "";
    float gps_lat_temp = 0;
    float gps_long_temp = 0;

    debug_print("\tGPS: satellites: ");
    gps_chars = gps.satellites();
    if (gps_chars == 255){gps_chars = 0;}
    dtostrf(gps_chars,0,0,gps_temp_string);
    debug_print(gps_temp_string);  
    unsigned long gps_fix_age_temp = 0;
    gps.f_get_position(&gps_lat_temp,&gps_long_temp,&gps_fix_age_temp); 
    debug_print(" lat: ");
    debug_print_float(gps_lat_temp,4);
    debug_print(" long: ");
    debug_print_float(gps_long_temp,4);
    debug_print(" fix age (mS): ");
    dtostrf(gps_fix_age_temp,0,0,gps_temp_string);
    debug_print(gps_temp_string);   
    gps.stats(&gps_chars,&gps_good_sentences,&gps_failed_checksum);     
    debug_print(" data chars: ");
    dtostrf(gps_chars,0,0,gps_temp_string);
    debug_print(gps_temp_string);
    debug_print(" good sentences: ");
    dtostrf(gps_good_sentences,0,0,gps_temp_string);
    debug_print(gps_temp_string);    
    debug_print(" failed checksum: ");
    dtostrf(gps_failed_checksum,0,0,gps_temp_string);
    debug_print(gps_temp_string);    
    debug_println("");
    #endif //FEATURE_GPS


    #ifdef FEATURE_AUTOCORRECT
    debug_print("\t\tAutocorrect: AZ:");
    switch(autocorrect_state_az){
      case AUTOCORRECT_INACTIVE: debug_print("INACTIVE"); break;
      case AUTOCORRECT_WAITING_AZ: debug_print("AUTOCORRECT_WAITING_AZ: "); debug_print_float(autocorrect_az,2); break;
      case AUTOCORRECT_WATCHING_AZ: debug_print("AUTOCORRECT_WATCHING_AZ: "); debug_print_float(autocorrect_az,2); break;
    }

    #ifdef FEATURE_ELEVATION_CONTROL
    debug_print(" EL:");
    switch(autocorrect_state_el){
      case AUTOCORRECT_INACTIVE: debug_print("INACTIVE"); break;
      case AUTOCORRECT_WAITING_EL: debug_print("AUTOCORRECT_WAITING_EL: "); debug_print_float(autocorrect_el,2); break;
      case AUTOCORRECT_WATCHING_EL: debug_print("AUTOCORRECT_WATCHING_EL: "); debug_print_float(autocorrect_el,2); break;
    }
    #endif //FEATURE_ELEVATION_CONTROL
    #endif //DEBUG_AUTOCORRECT



    debug_println("\n\n\n");

    //port_flush();

    last_debug_output_time = millis();

    

  }
  #endif // defined(FEATURE_REMOTE_UNIT_SLAVE) || defined(FEATURE_YAESU_EMULATION) || defined(FEATURE_EASYCOM_EMULATION)
  
  #endif //DEBUG_DUMP

} /* output_debug */

// --------------------------------------------------------------


// void report_current_azimuth() {

//   #ifdef FEATURE_YAESU_EMULATION
//   // The C command that reports azimuth

//   String azimuth_string;

//   #ifndef OPTION_GS_232B_EMULATION
//   control_port->print(F("+0"));
//   #endif
//   #ifdef OPTION_GS_232B_EMULATION
//   control_port->print(F("AZ="));
//   #endif
//   // control_port->write("report_current_azimith: azimuth=");
//   // control_port->println(azimuth);
//   azimuth_string = String(int(azimuth / HEADING_MULTIPLIER), DEC);
//   if (azimuth_string.length() == 1) {
//     control_port->print(F("00"));
//   } else {
//     if (azimuth_string.length() == 2) {
//       control_port->print(F("0"));
//     }
//   }
//   control_port->print(azimuth_string);

//     #ifdef FEATURE_ELEVATION_CONTROL
//     #ifndef OPTION_C_COMMAND_SENDS_AZ_AND_EL
//   if ((control_port_buffer[1] == '2') && (control_port_buffer_index > 1)) {     // did we get the C2 command?
//     #endif
//   report_current_elevation();
//     #ifndef OPTION_C_COMMAND_SENDS_AZ_AND_EL
// } else {
//   control_port->println();
// }
//     #endif // OPTION_C_COMMAND_SENDS_AZ_AND_EL
//   #endif // FEATURE_ELEVATION_CONTROL

//   #ifndef FEATURE_ELEVATION_CONTROL
//   if ((control_port_buffer[1] == '2') && (control_port_buffer_index > 1)) {     // did we get the C2 command?
//     #ifndef OPTION_GS_232B_EMULATION
//     control_port->println(F("+0000"));    // return a dummy elevation since we don't have the elevation feature turned on
//     #else
//     control_port->println(F("EL=000"));
//     #endif
//   } else {
//     control_port->println();
//   }
//     #endif // FEATURE_ELEVATION_CONTROL



//   #endif // FEATURE_YAESU_EMULATION
// }


// --------------------------------------------------------------
void print_to_port(char * print_this,byte port){

  #if defined(FEATURE_REMOTE_UNIT_SLAVE) || defined(FEATURE_YAESU_EMULATION) || defined(FEATURE_EASYCOM_EMULATION)
  
  switch(port){
    case CONTROL_PORT0: control_port->println(print_this);break;
    #ifdef FEATURE_ETHERNET
    case ETHERNET_PORT0: ethernetclient0.print(print_this); break;
    #ifdef ETHERNET_TCP_PORT_1
    case ETHERNET_PORT1: ethernetclient1.print(print_this); break;
    #endif //ETHERNET_TCP_PORT_1
    #endif //FEATURE_ETHERNET
  }
  
  #endif //defined(FEATURE_REMOTE_UNIT_SLAVE) || defined(FEATURE_YAESU_EMULATION) || defined(FEATURE_EASYCOM_EMULATION)

}


// --------------------------------------------------------------
void print_help(byte port){

  // The H command

  #if defined(OPTION_SERIAL_HELP_TEXT) && defined(FEATURE_YAESU_EMULATION)
  print_to_port("R Rotate Azimuth Clockwise\n",port);
  print_to_port("L Rotate Azimuth Counter Clockwise\n",port);
  print_to_port("A Stop\n",port);
  print_to_port("C Report Azimuth in Degrees\n",port);
  print_to_port("M### Rotate to ### degrees\n",port);
  print_to_port("MTTT XXX XXX XXX ... Timed Interval Direction Setting  (TTT = Step value in seconds, XXX = Azimuth in degrees)\n",port);
  print_to_port("T Start Timed Interval Tracking\n",port);
  print_to_port("N Report Total Number of M Timed Interval Azimuths\n",port);
  print_to_port("X1 Horizontal Rotation Low Speed\n",port);
  print_to_port("X2 Horizontal Rotation Middle 1 Speed\n",port);
  print_to_port("X3 Horizontal Rotation Middle 2 Speed\n",port);
  print_to_port("X4 Horizontal Rotation High Speed\n",port);
  print_to_port("S Stop\n",port);
  print_to_port("O Offset Calibration\n",port);
  print_to_port("F Full Scale Calibration\n",port);
  #ifdef FEATURE_ELEVATION_CONTROL
  print_to_port("U Rotate Elevation Up\n",port);
  print_to_port("D Rotate Elevation Down\n",port);
  print_to_port("E Stop Elevation Rotation\n",port);
  print_to_port("B Report Elevation in Degrees\n",port);
  print_to_port("Wxxx yyy Rotate Azimuth to xxx Degrees and Elevation to yyy Degrees\n",port);
  print_to_port("O2 Elevation Offset Calibration (0 degrees)\n",port);
  print_to_port("F2 Elevation Full Scale Calibration (180 degrees (or maximum))\n",port);
  #endif // FEATURE_ELEVATION_CONTROL
  #endif // defined(OPTION_SERIAL_HELP_TEXT) && defined(FEATURE_YAESU_EMULATION)


} /* print_help */

// --------------- Elevation -----------------------

#ifdef FEATURE_ELEVATION_CONTROL
void el_check_operation_timeout(){

  // check if the last executed rotation operation has been going on too long

  if (((millis() - el_last_rotate_initiation) > OPERATION_TIMEOUT) && (el_state != IDLE)) {
    submit_request(EL, REQUEST_KILL, 0, 85);
    #ifdef DEBUG_EL_CHECK_OPERATION_TIMEOUT
    if (debug_mode) {
      control_port->println(F("el_check_operation_timeout: timeout reached, aborting rotation"));
    }
    #endif // DEBUG_EL_CHECK_OPERATION_TIMEOUT
  }
}
#endif


// --------------------------------------------------------------

#ifdef FEATURE_ELEVATION_CONTROL
void read_elevation(byte force_read){

  #ifdef FEATURE_EL_POSITION_INCREMENTAL_ENCODER
  read_elevation_lock = 1;
  #endif


  unsigned int previous_elevation = elevation;
  static unsigned long last_measurement_time = 0;

  #ifdef FEATURE_EL_POSITION_INCREMENTAL_ENCODER
  static unsigned int incremental_encoder_previous_elevation = elevation;
  #endif

  if (heading_reading_inhibit_pin) {
    if (digitalReadEnhanced(heading_reading_inhibit_pin)) {
      return;
    }
  }

  #ifdef DEBUG_HEADING_READING_TIME
  static unsigned long last_time = 0;
  static unsigned long last_print_time = 0;
  static float average_read_time = 0;
  #endif // DEBUG_HEADING_READING_TIME

  #ifdef DEBUG_HH12
  static unsigned long last_hh12_debug = 0;
  #endif // DEBUG_HH12

  #ifndef FEATURE_EL_POSITION_GET_FROM_REMOTE_UNIT
  if (((millis() - last_measurement_time) > ELEVATION_MEASUREMENT_FREQUENCY_MS) || (force_read)) {
  #else
  if (1) {
  #endif

    #ifdef FEATURE_EL_POSITION_POTENTIOMETER
    analog_el = analogReadEnhanced(rotator_analog_el);
    elevation = (map(analog_el, configuration.analog_el_0_degrees, configuration.analog_el_max_elevation, 0, (ELEVATION_MAXIMUM_DEGREES * HEADING_MULTIPLIER)));
    #ifdef FEATURE_ELEVATION_CORRECTION
    elevation = (correct_elevation(elevation / (float) HEADING_MULTIPLIER) * HEADING_MULTIPLIER);
    #endif // FEATURE_ELEVATION_CORRECTION
    elevation = elevation + (configuration.elevation_offset * HEADING_MULTIPLIER);
    if (ELEVATION_SMOOTHING_FACTOR > 0) {
      elevation = (elevation * (1 - (ELEVATION_SMOOTHING_FACTOR / 100))) + (previous_elevation * (ELEVATION_SMOOTHING_FACTOR / 100));
    }
    if (elevation < 0) {
      elevation = 0;
    }
    #endif // FEATURE_EL_POSITION_POTENTIOMETER


    #ifdef FEATURE_EL_POSITION_ROTARY_ENCODER
    static byte el_position_encoder_state = 0;
    el_position_encoder_state = ttable[el_position_encoder_state & 0xf][((digitalReadEnhanced(el_rotary_position_pin2) << 1) | digitalReadEnhanced(el_rotary_position_pin1))];
    byte el_position_encoder_result = el_position_encoder_state & 0x30;
    if (el_position_encoder_result) {
      if (el_position_encoder_result == DIR_CW) {
        configuration.last_elevation = configuration.last_elevation + EL_POSITION_ROTARY_ENCODER_DEG_PER_PULSE;
        #ifdef DEBUG_POSITION_ROTARY_ENCODER
        if (debug_mode) {
          control_port->println(F("read_elevation: EL_POSITION_ROTARY_ENCODER: CW/UP"));
        }
        #endif // DEBUG_POSITION_ROTARY_ENCODER
      }
      if (el_position_encoder_result == DIR_CCW) {
        configuration.last_elevation = configuration.last_elevation - EL_POSITION_ROTARY_ENCODER_DEG_PER_PULSE;
        #ifdef DEBUG_POSITION_ROTARY_ENCODER
        if (debug_mode) {
          control_port->println(F("read_elevation: EL_POSITION_ROTARY_ENCODER: CCW/DWN"));
        }
        #endif // DEBUG_POSITION_ROTARY_ENCODER
      }
        #ifdef OPTION_EL_POSITION_ROTARY_ENCODER_HARD_LIMIT
      if (configuration.last_elevation < 0) {
        configuration.last_elevation = 0;
      }
      if (configuration.last_elevation > ELEVATION_MAXIMUM_DEGREES) {
        configuration.last_elevation = ELEVATION_MAXIMUM_DEGREES;
      }
      #endif
      elevation = int(configuration.last_elevation * HEADING_MULTIPLIER);
      #ifdef FEATURE_ELEVATION_CORRECTION
      elevation = (correct_elevation(elevation / (float) HEADING_MULTIPLIER) * HEADING_MULTIPLIER);
      #endif // FEATURE_ELEVATION_CORRECTION
      configuration_dirty = 1;
    }
    #endif // FEATURE_EL_POSITION_ROTARY_ENCODER


    #ifdef FEATURE_EL_POSITION_ADXL345_USING_LOVE_ELECTRON_LIB
    AccelerometerRaw raw = accel.ReadRawAxis();
    AccelerometerScaled scaled = accel.ReadScaledAxis();
    #ifdef DEBUG_ACCEL
    if (debug_mode) {
      control_port->print(F("read_elevation: raw.ZAxis: "));
      control_port->println(raw.ZAxis);
    }
    #endif // DEBUG_ACCEL
    elevation = (atan2(scaled.YAxis, scaled.ZAxis) * 180 * HEADING_MULTIPLIER) / M_PI;
    #ifdef FEATURE_ELEVATION_CORRECTION
    elevation = (correct_elevation(elevation / (float) HEADING_MULTIPLIER) * HEADING_MULTIPLIER);
    #endif // FEATURE_ELEVATION_CORRECTION
    elevation = elevation + (configuration.elevation_offset * HEADING_MULTIPLIER);
    if (ELEVATION_SMOOTHING_FACTOR > 0) {
      elevation = (elevation * (1 - (ELEVATION_SMOOTHING_FACTOR / 100))) + (previous_elevation * (ELEVATION_SMOOTHING_FACTOR / 100));
    }
    #endif // FEATURE_EL_POSITION_ADXL345_USING_LOVE_ELECTRON_LIB

    #ifdef FEATURE_EL_POSITION_ADXL345_USING_ADAFRUIT_LIB
    sensors_event_t event;
    accel.getEvent(&event);
    #ifdef DEBUG_ACCEL
    if (debug_mode) {
      control_port->print(F("read_elevation: event.acceleration.z: "));
      control_port->println(event.acceleration.z);
    }
    #endif // DEBUG_ACCEL
    elevation = (atan2(event.acceleration.y, event.acceleration.z) * 180 * HEADING_MULTIPLIER) / M_PI;
    #ifdef FEATURE_ELEVATION_CORRECTION
    elevation = (correct_elevation(elevation / (float) HEADING_MULTIPLIER) * HEADING_MULTIPLIER);
    #endif // FEATURE_ELEVATION_CORRECTION
    elevation = elevation + (configuration.elevation_offset * HEADING_MULTIPLIER);
    #endif // FEATURE_EL_POSITION_ADXL345_USING_ADAFRUIT_LIB



    #ifdef FEATURE_EL_POSITION_ADAFRUIT_LSM303
    lsm.read();
    #ifdef DEBUG_ACCEL
    if (debug_mode) {
      control_port->print(F("read_elevation: lsm.accelData.y: "));
      control_port->print(lsm.accelData.y);
      control_port->print(F(" lsm.accelData.z: "));
      control_port->println(lsm.accelData.z);
    }
    #endif // DEBUG_ACCEL
    elevation = (atan2(lsm.accelData.y, lsm.accelData.z) * 180 * HEADING_MULTIPLIER) / M_PI;
    #ifdef FEATURE_ELEVATION_CORRECTION
    elevation = (correct_elevation(elevation / (float) HEADING_MULTIPLIER) * HEADING_MULTIPLIER);
    #endif // FEATURE_ELEVATION_CORRECTION
    elevation = elevation + (configuration.elevation_offset * HEADING_MULTIPLIER);
    #endif // FEATURE_EL_POSITION_ADAFRUIT_LSM303

    #ifdef FEATURE_EL_POSITION_POLOLU_LSM303
    compass.read();
    #ifdef DEBUG_ACCEL
    if (debug_mode) {
      control_port->print(F("read_elevation: compass.a.y: "));
      control_port->print(compass.a.y);
      control_port->print(F(" compass.a.z: "));
      control_port->println(compass.a.z);
    }
    #endif // DEBUG_ACCEL
    elevation = (atan2(compass.a.x, compass.a.z) * -180 * HEADING_MULTIPLIER) / M_PI; //lsm.accelData.y
    #ifdef FEATURE_ELEVATION_CORRECTION
    elevation = (correct_elevation(elevation / (float) HEADING_MULTIPLIER) * HEADING_MULTIPLIER);
    #endif // FEATURE_ELEVATION_CORRECTION
    elevation = elevation + (configuration.elevation_offset * HEADING_MULTIPLIER);
    #endif // FEATURE_EL_POSITION_POLOLU_LSM303


    #ifdef FEATURE_EL_POSITION_PULSE_INPUT
    #ifdef DEBUG_POSITION_PULSE_INPUT
    //    if (el_position_pule_interrupt_handler_flag) {
    //      control_port->print(F("read_elevation: el_position_pule_interrupt_handler_flag: "));
    //      control_port->println(el_position_pule_interrupt_handler_flag);
    //      el_position_pule_interrupt_handler_flag = 0;
    //    }
    #endif // DEBUG_POSITION_PULSE_INPUT

    static float last_el_position_pulse_input_elevation = el_position_pulse_input_elevation;

    if (el_position_pulse_input_elevation != last_el_position_pulse_input_elevation) {
      #ifdef DEBUG_POSITION_PULSE_INPUT
      //      if (debug_mode){
      //        control_port->print(F("read_elevation: el_position_pulse_input_elevation:"));
      //        control_port->println(el_position_pulse_input_elevation);
      //      }
      #endif // DEBUG_POSITION_PULSE_INPUT
      configuration.last_elevation = el_position_pulse_input_elevation;
      configuration_dirty = 1;
      last_el_position_pulse_input_elevation = el_position_pulse_input_elevation;
      elevation = int(configuration.last_elevation * HEADING_MULTIPLIER);
      #ifdef FEATURE_ELEVATION_CORRECTION
      elevation = (correct_elevation(elevation / (float) HEADING_MULTIPLIER) * HEADING_MULTIPLIER);
      #endif FEATURE_ELEVATION_CORRECTION
      elevation = elevation + (configuration.elevation_offset * HEADING_MULTIPLIER);
    }
    #endif // FEATURE_EL_POSITION_PULSE_INPUT

    #ifdef FEATURE_EL_POSITION_GET_FROM_REMOTE_UNIT
    #if defined(FEATURE_MASTER_WITH_SERIAL_SLAVE) || defined(FEATURE_MASTER_WITH_ETHERNET_SLAVE)
    static unsigned long last_remote_unit_el_query_time = 0;
    // do we have a command result waiting for us?
    if (remote_unit_command_results_available == REMOTE_UNIT_EL_COMMAND) {

      #ifdef DEBUG_HEADING_READING_TIME
      average_read_time = (average_read_time + (millis() - last_time)) / 2.0;
      last_time = millis();

      if (debug_mode) {
        if ((millis() - last_print_time) > 1000) {
          control_port->print(F("read_elevation: avg read frequency: "));
          control_port->println(average_read_time, 2);
          last_print_time = millis();
        }
      }
      #endif // DEBUG_HEADING_READING_TIME
      elevation = remote_unit_command_result_float * HEADING_MULTIPLIER;
      #ifdef FEATURE_ELEVATION_CORRECTION
      elevation = (correct_elevation(elevation / (float) HEADING_MULTIPLIER) * HEADING_MULTIPLIER);
      #endif // FEATURE_ELEVATION_CORRECTION
      elevation = elevation + (configuration.elevation_offset * HEADING_MULTIPLIER);
      if (ELEVATION_SMOOTHING_FACTOR > 0) {
        elevation = (elevation * (1 - (ELEVATION_SMOOTHING_FACTOR / 100))) + (previous_elevation * (ELEVATION_SMOOTHING_FACTOR / 100));
      }
      remote_unit_command_results_available = 0;
    } else {
      // is it time to request the elevation?
      if ((millis() - last_remote_unit_el_query_time) > EL_REMOTE_UNIT_QUERY_TIME_MS) {
        if (submit_remote_command(REMOTE_UNIT_EL_COMMAND, 0, 0)) {
          last_remote_unit_el_query_time = millis();
        }
      }
    }
    #endif // defined(FEATURE_MASTER_WITH_SERIAL_SLAVE) || defined(FEATURE_MASTER_WITH_ETHERNET_SLAVE)
    #endif // FEATURE_EL_POSITION_GET_FROM_REMOTE_UNIT


    #ifdef FEATURE_EL_POSITION_HH12_AS5045_SSI
    elevation = int(elevation_hh12.heading() * HEADING_MULTIPLIER);
    #ifdef DEBUG_HH12
    if ((millis() - last_hh12_debug) > 5000) {
      control_port->print(F("read_elevation: HH-12 raw: "));
      control_port->println(elevation);
      last_hh12_debug = millis();
    }
    #endif // DEBUG_HH12
    #ifdef FEATURE_ELEVATION_CORRECTION
    elevation = (correct_elevation(elevation / (float) HEADING_MULTIPLIER) * HEADING_MULTIPLIER);
    #endif // FEATURE_ELEVATION_CORRECTION
    elevation = elevation + (configuration.elevation_offset * HEADING_MULTIPLIER);
    if (elevation > (180 * HEADING_MULTIPLIER)) {
      elevation = elevation - (360 * HEADING_MULTIPLIER);
    }
    #endif // FEATURE_EL_POSITION_HH12_AS5045_SSI


    #ifdef FEATURE_EL_POSITION_INCREMENTAL_ENCODER
    elevation = ((((el_incremental_encoder_position) / (EL_POSITION_INCREMENTAL_ENCODER_PULSES_PER_REV*4.)) * 360.0) * HEADING_MULTIPLIER);
    #ifdef FEATURE_ELEVATION_CORRECTION
    elevation = (correct_elevation(elevation / (float) HEADING_MULTIPLIER) * HEADING_MULTIPLIER);
    #endif // FEATURE_ELEVATION_CORRECTION
    if (incremental_encoder_previous_elevation != elevation) {
      configuration.last_el_incremental_encoder_position = el_incremental_encoder_position;
      configuration_dirty = 1;
      incremental_encoder_previous_elevation = elevation;
    }
    elevation = elevation + (configuration.elevation_offset * HEADING_MULTIPLIER);
    #endif // FEATURE_EL_POSITION_INCREMENTAL_ENCODER

    #ifdef FEATURE_EL_POSITION_MEMSIC_2125
    unsigned int pulseY = pulseIn(pin_memsic_2125_y,HIGH,250000);
    pulseY = pulseIn(pin_memsic_2125_y,HIGH,250000);
    double Yangle = (asin(((( pulseY / 10. ) - 500.) * 8.) / 1000.0 )) * (360. / (2. * M_PI));
    #ifdef DEBUG_MEMSIC_2125
    debug_print("read_elevation: memsic2125 pulseY:");
    debug_print_int(pulseY);
    debug_println("");
    #endif //DEBUG_MEMSIC_2125
    elevation = Yangle * HEADING_MULTIPLIER;    
    #ifdef FEATURE_ELEVATION_CORRECTION
    elevation = (correct_elevation(elevation / (float) HEADING_MULTIPLIER) * HEADING_MULTIPLIER);
    #endif //FEATURE_ELEVATION_CORRECTION
    #endif //FEATURE_EL_POSITION_MEMSIC_2125

    last_measurement_time = millis();
  }




  #ifdef FEATURE_EL_POSITION_INCREMENTAL_ENCODER
  read_elevation_lock = 0;
  #endif


} /* read_elevation */
#endif /* ifdef FEATURE_ELEVATION_CONTROL */


// --------------------------------------------------------------
#ifdef FEATURE_ELEVATION_CONTROL
void update_el_variable_outputs(byte speed_voltage){


  #ifdef DEBUG_VARIABLE_OUTPUTS
  debug_print("update_el_variable_outputs: speed_voltage: ");
  debug_print_int(speed_voltage);
  #endif // DEBUG_VARIABLE_OUTPUTS

  if (((el_state == SLOW_START_UP) || (el_state == NORMAL_UP) || (el_state == SLOW_DOWN_UP) || (el_state == TIMED_SLOW_DOWN_UP)) && (rotate_up_pwm)) {
    #ifdef DEBUG_VARIABLE_OUTPUTS
    debug_print("\trotate_up_pwm");
    #endif // DEBUG_VARIABLE_OUTPUTS
    analogWriteEnhanced(rotate_up_pwm, speed_voltage);
  }

  if (((el_state == SLOW_START_DOWN) || (el_state == NORMAL_DOWN) || (el_state == SLOW_DOWN_DOWN) || (el_state == TIMED_SLOW_DOWN_DOWN)) && (rotate_down_pwm)) {
    #ifdef DEBUG_VARIABLE_OUTPUTS
    debug_print("\trotate_down_pwm");
    #endif // DEBUG_VARIABLE_OUTPUTS
    analogWriteEnhanced(rotate_down_pwm, speed_voltage);
  }

  if (((el_state == SLOW_START_DOWN) || (el_state == NORMAL_DOWN) || (el_state == SLOW_DOWN_DOWN) || (el_state == TIMED_SLOW_DOWN_DOWN) ||
       (el_state == SLOW_START_UP) || (el_state == NORMAL_UP) || (el_state == SLOW_DOWN_UP) || (el_state == TIMED_SLOW_DOWN_UP)) && (rotate_up_down_pwm)) {
    #ifdef DEBUG_VARIABLE_OUTPUTS
    debug_print("\trotate_up_down_pwm");
    #endif // DEBUG_VARIABLE_OUTPUTS
    analogWriteEnhanced(rotate_up_down_pwm, speed_voltage);
  }


  if (((el_state == SLOW_START_UP) || (el_state == NORMAL_UP) || (el_state == SLOW_DOWN_UP) || (el_state == TIMED_SLOW_DOWN_UP)) && (rotate_up_freq)) {
    #ifdef DEBUG_VARIABLE_OUTPUTS
    debug_print("\trotate_up_freq");
    #endif // DEBUG_VARIABLE_OUTPUTS
    tone(rotate_up_freq, map(speed_voltage, 0, 255, EL_VARIABLE_FREQ_OUTPUT_LOW, EL_VARIABLE_FREQ_OUTPUT_HIGH));
  }

  if (((el_state == SLOW_START_DOWN) || (el_state == NORMAL_DOWN) || (el_state == SLOW_DOWN_DOWN) || (el_state == TIMED_SLOW_DOWN_DOWN)) && (rotate_down_freq)) {
    #ifdef DEBUG_VARIABLE_OUTPUTS
    debug_print("\trotate_down_freq");
    #endif // DEBUG_VARIABLE_OUTPUTS
    tone(rotate_down_freq, map(speed_voltage, 0, 255, EL_VARIABLE_FREQ_OUTPUT_LOW, EL_VARIABLE_FREQ_OUTPUT_HIGH));
  }

  #ifdef FEATURE_STEPPER_MOTOR

  unsigned int el_tone = 0;

  if (((el_state == SLOW_START_UP) || (el_state == NORMAL_UP) || (el_state == SLOW_DOWN_UP) || (el_state == TIMED_SLOW_DOWN_UP) || (el_state == SLOW_START_DOWN) || (el_state == NORMAL_DOWN) || (el_state == SLOW_DOWN_DOWN) || (el_state == TIMED_SLOW_DOWN_DOWN)) && (el_stepper_motor_pulse)) {
    #ifdef DEBUG_VARIABLE_OUTPUTS
    debug_print("\tel_stepper_motor_pulse: ");
    #endif // DEBUG_VARIABLE_OUTPUTS
    el_tone = map(speed_voltage, 0, 255, EL_VARIABLE_FREQ_OUTPUT_LOW, EL_VARIABLE_FREQ_OUTPUT_HIGH);

    #ifdef FEATURE_STEPPER_MOTOR
    set_el_stepper_freq(el_tone);
    #endif




    #ifdef DEBUG_VARIABLE_OUTPUTS
    debug_print_int(el_tone);
    #endif // DEBUG_VARIABLE_OUTPUTS

  }
  #endif //FEATURE_STEPPER_MOTOR

  if (elevation_speed_voltage) {
    analogWriteEnhanced(elevation_speed_voltage, speed_voltage);
  }

  #ifdef DEBUG_VARIABLE_OUTPUTS
  debug_println("");
  #endif // DEBUG_VARIABLE_OUTPUTS

  current_el_speed_voltage = speed_voltage;


} /* update_el_variable_outputs */
#endif // FEATURE_ELEVATION_CONTROL

// --------------------------------------------------------------
void update_az_variable_outputs(byte speed_voltage){


  #ifdef DEBUG_VARIABLE_OUTPUTS  
  int temp_int = 0;

  debug_print("update_az_variable_outputs: az_state: ");
  switch (az_state) {
    case IDLE: debug_print("IDLE"); break;
    case SLOW_START_CW: debug_print("SLOW_START_CW"); break;
    case SLOW_START_CCW: debug_print("SLOW_START_CCW"); break;
    case NORMAL_CW: debug_print("NORMAL_CW"); break;
    case NORMAL_CCW: debug_print("NORMAL_CCW"); break;
    case SLOW_DOWN_CW: debug_print("SLOW_DOWN_CW"); break;
    case SLOW_DOWN_CCW: debug_print("SLOW_DOWN_CCW"); break;
    case INITIALIZE_SLOW_START_CW: debug_print("INITIALIZE_SLOW_START_CW"); break;
    case INITIALIZE_SLOW_START_CCW: debug_print("INITIALIZE_SLOW_START_CCW"); break;
    case INITIALIZE_TIMED_SLOW_DOWN_CW: debug_print("INITIALIZE_TIMED_SLOW_DOWN_CW"); break;
    case INITIALIZE_TIMED_SLOW_DOWN_CCW: debug_print("INITIALIZE_TIMED_SLOW_DOWN_CCW"); break;
    case TIMED_SLOW_DOWN_CW: debug_print("TIMED_SLOW_DOWN_CW"); break;
    case TIMED_SLOW_DOWN_CCW: debug_print("TIMED_SLOW_DOWN_CCW"); break;
    case INITIALIZE_DIR_CHANGE_TO_CW: debug_print("INITIALIZE_DIR_CHANGE_TO_CW"); break;
    case INITIALIZE_DIR_CHANGE_TO_CCW: debug_print("INITIALIZE_DIR_CHANGE_TO_CCW"); break;
    case INITIALIZE_NORMAL_CW: debug_print("INITIALIZE_NORMAL_CW"); break;
    case INITIALIZE_NORMAL_CCW: debug_print("INITIALIZE_NORMAL_CCW"); break;
    default: debug_print("UNDEF"); break;
  }
  debug_print(" speed_voltage: ");
  debug_print_int(speed_voltage);
  #endif // DEBUG_VARIABLE_OUTPUTS

  if (((az_state == SLOW_START_CW) || (az_state == NORMAL_CW) || (az_state == SLOW_DOWN_CW) || (az_state == TIMED_SLOW_DOWN_CW)) && (rotate_cw_pwm)) {
    #ifdef DEBUG_VARIABLE_OUTPUTS
    debug_print("\trotate_cw_pwm");
    #endif // DEBUG_VARIABLE_OUTPUTS
    analogWriteEnhanced(rotate_cw_pwm, speed_voltage);
  }

  if (((az_state == SLOW_START_CCW) || (az_state == NORMAL_CCW) || (az_state == SLOW_DOWN_CCW) || (az_state == TIMED_SLOW_DOWN_CCW)) && (rotate_ccw_pwm)) {
    #ifdef DEBUG_VARIABLE_OUTPUTS
    debug_print("\trotate_ccw_pwm");
    #endif // DEBUG_VARIABLE_OUTPUTS
    analogWriteEnhanced(rotate_ccw_pwm, speed_voltage);
  }

  if (((az_state == SLOW_START_CW) || (az_state == NORMAL_CW) || (az_state == SLOW_DOWN_CW) || (az_state == TIMED_SLOW_DOWN_CW) || (az_state == SLOW_START_CCW) || (az_state == NORMAL_CCW) || (az_state == SLOW_DOWN_CCW) || (az_state == TIMED_SLOW_DOWN_CCW)) && (rotate_cw_ccw_pwm)) {
    #ifdef DEBUG_VARIABLE_OUTPUTS
    debug_print("\trotate_cw_ccw_pwm");
    #endif // DEBUG_VARIABLE_OUTPUTS
    analogWriteEnhanced(rotate_cw_ccw_pwm, speed_voltage);
  }


  if (((az_state == SLOW_START_CW) || (az_state == NORMAL_CW) || (az_state == SLOW_DOWN_CW) || (az_state == TIMED_SLOW_DOWN_CW)) && (rotate_cw_freq)) {
    #ifdef DEBUG_VARIABLE_OUTPUTS
    debug_print("\trotate_cw_freq: ");
    temp_int = map(speed_voltage, 0, 255, AZ_VARIABLE_FREQ_OUTPUT_LOW, AZ_VARIABLE_FREQ_OUTPUT_HIGH);
    tone(rotate_cw_freq, temp_int);
    debug_print_int(temp_int);
    #else // DEBUG_VARIABLE_OUTPUTS
    tone(rotate_cw_freq, map(speed_voltage, 0, 255, AZ_VARIABLE_FREQ_OUTPUT_LOW, AZ_VARIABLE_FREQ_OUTPUT_HIGH));
    #endif // DEBUG_VARIABLE_OUTPUTS
  }

  if (((az_state == SLOW_START_CCW) || (az_state == NORMAL_CCW) || (az_state == SLOW_DOWN_CCW) || (az_state == TIMED_SLOW_DOWN_CCW)) && (rotate_ccw_freq)) {
    #ifdef DEBUG_VARIABLE_OUTPUTS
    debug_print("\trotate_ccw_freq: ");
    temp_int = map(speed_voltage, 0, 255, AZ_VARIABLE_FREQ_OUTPUT_LOW, AZ_VARIABLE_FREQ_OUTPUT_HIGH);
    tone(rotate_ccw_freq, temp_int);
    debug_print_int(temp_int);    
    #else // DEBUG_VARIABLE_OUTPUTS
    tone(rotate_ccw_freq, map(speed_voltage, 0, 255, AZ_VARIABLE_FREQ_OUTPUT_LOW, AZ_VARIABLE_FREQ_OUTPUT_HIGH));
    #endif // DEBUG_VARIABLE_OUTPUTS
  }

  #ifdef FEATURE_STEPPER_MOTOR

  unsigned int az_tone = 0;

  if (((az_state == SLOW_START_CW) || (az_state == NORMAL_CW) || (az_state == SLOW_DOWN_CW) || (az_state == TIMED_SLOW_DOWN_CW) || (az_state == SLOW_START_CCW) || (az_state == NORMAL_CCW) || (az_state == SLOW_DOWN_CCW) || (az_state == TIMED_SLOW_DOWN_CCW)) && (az_stepper_motor_pulse)) {
    #ifdef DEBUG_VARIABLE_OUTPUTS
    debug_print("\taz_stepper_motor_pulse: ");
    #endif // DEBUG_VARIABLE_OUTPUTS
    az_tone = map(speed_voltage, 0, 255, AZ_VARIABLE_FREQ_OUTPUT_LOW, AZ_VARIABLE_FREQ_OUTPUT_HIGH);
    set_az_stepper_freq(az_tone);
    #ifdef DEBUG_VARIABLE_OUTPUTS
    debug_print_int(az_tone);
    #endif // DEBUG_VARIABLE_OUTPUTS 
  }
  #endif //FEATURE_STEPPER_MOTOR

  if (azimuth_speed_voltage) {
    analogWriteEnhanced(azimuth_speed_voltage, speed_voltage);
  }

  #ifdef DEBUG_VARIABLE_OUTPUTS
  debug_println("");
  #endif // DEBUG_VARIABLE_OUTPUTS

  current_az_speed_voltage = speed_voltage;

} /* update_az_variable_outputs */

// --------------------------------------------------------------

void rotator(byte rotation_action, byte rotation_type) {

  #ifdef DEBUG_ROTATOR
  if (debug_mode) {
    control_port->flush();
    control_port->print(F("rotator: rotation_action:"));
    control_port->print(rotation_action);
    control_port->print(F(" rotation_type:"));
    control_port->flush();
    control_port->print(rotation_type);
    control_port->print(F("->"));
    control_port->flush();
    // delay(1000);
  }
  #endif // DEBUG_ROTATOR

  switch (rotation_type) {
    case CW:
    #ifdef DEBUG_ROTATOR
      if (debug_mode) {
        control_port->print(F("CW ")); control_port->flush();
      }
    #endif // DEBUG_ROTATOR
      if (rotation_action == ACTIVATE) {
      #ifdef DEBUG_ROTATOR
        if (debug_mode) {
          control_port->println(F("ACTIVATE")); control_port->flush();
        }
      #endif // DEBUG_ROTATOR
        brake_release(AZ, BRAKE_RELEASE_ON);
        if (az_slowstart_active) {
          if (rotate_cw_pwm) {
            analogWriteEnhanced(rotate_cw_pwm, 0);
          }
          if (rotate_ccw_pwm) {
            analogWriteEnhanced(rotate_ccw_pwm, 0); digitalWriteEnhanced(rotate_ccw_pwm, LOW);
          }
          if (rotate_cw_ccw_pwm) {
            analogWriteEnhanced(rotate_cw_ccw_pwm, 0);
          }
          if (rotate_cw_freq) {
            noTone(rotate_cw_freq);
          }
          if (rotate_ccw_freq) {
            noTone(rotate_ccw_freq);
          }       
          #ifdef FEATURE_STEPPER_MOTOR
          if (az_stepper_motor_pulse) {
            set_az_stepper_freq(0);
            digitalWriteEnhanced(az_stepper_motor_pulse,LOW);
          }      
          #endif //FEATURE_STEPPER_MOTOR

        } else {
          if (rotate_cw_pwm) {
            analogWriteEnhanced(rotate_cw_pwm, normal_az_speed_voltage);
          }
          if (rotate_ccw_pwm) {
            analogWriteEnhanced(rotate_ccw_pwm, 0); digitalWriteEnhanced(rotate_ccw_pwm, LOW);
          }
          if (rotate_cw_ccw_pwm) {
            analogWriteEnhanced(rotate_cw_ccw_pwm, normal_az_speed_voltage);
          }
          if (rotate_cw_freq) {
            tone(rotate_cw_freq, map(normal_az_speed_voltage, 0, 255, AZ_VARIABLE_FREQ_OUTPUT_LOW, AZ_VARIABLE_FREQ_OUTPUT_HIGH));
          }
          if (rotate_ccw_freq) {
            noTone(rotate_ccw_freq);
          }  
          #ifdef FEATURE_STEPPER_MOTOR
          if (az_stepper_motor_pulse) {
            set_az_stepper_freq(map(normal_az_speed_voltage, 0, 255, AZ_VARIABLE_FREQ_OUTPUT_LOW, AZ_VARIABLE_FREQ_OUTPUT_HIGH));
          }
          #endif //FEATURE_STEPPER_MOTOR                 
        }
        if (rotate_cw) {
          digitalWriteEnhanced(rotate_cw, ROTATE_PIN_ACTIVE_VALUE);
        }
        if (rotate_ccw) {
          digitalWriteEnhanced(rotate_ccw, ROTATE_PIN_INACTIVE_VALUE);
        }
        if (rotate_cw_ccw){
          digitalWriteEnhanced(rotate_cw_ccw, ROTATE_PIN_ACTIVE_VALUE);
        }
        /*
        #ifdef FEATURE_STEPPER_MOTOR
        if (az_stepper_motor_direction){
          if (configuration.az_stepper_motor_last_direction != STEPPER_CW){
            if (configuration.az_stepper_motor_last_pin_state == LOW){
              digitalWriteEnhanced(az_stepper_motor_direction,HIGH);
              configuration.az_stepper_motor_last_pin_state = HIGH;
            } else {
              digitalWriteEnhanced(az_stepper_motor_direction,LOW);
              configuration.az_stepper_motor_last_pin_state = LOW;             
            }
            configuration.az_stepper_motor_last_direction = STEPPER_CW;
            configuration_dirty = 1;
          }
        }
        #endif //FEATURE_STEPPER_MOTOR
        */
        #ifdef DEBUG_ROTATOR
        if (debug_mode) {
          control_port->print(F("rotator: normal_az_speed_voltage:"));
          control_port->println(normal_az_speed_voltage);
          //control_port->flush();
        }
        #endif // DEBUG_ROTATOR
      } else {
          #ifdef DEBUG_ROTATOR
        if (debug_mode) {
          control_port->println(F("DEACTIVATE")); //control_port->flush();
        }
          #endif // DEBUG_ROTATOR
        if (rotate_cw_pwm) {
          analogWriteEnhanced(rotate_cw_pwm, 0); digitalWriteEnhanced(rotate_cw_pwm, LOW);
        }
        if (rotate_cw_ccw_pwm) {
          analogWriteEnhanced(rotate_cw_ccw_pwm, 0);
        }
        if (rotate_cw) {
          digitalWriteEnhanced(rotate_cw, ROTATE_PIN_INACTIVE_VALUE);
        }
        if (rotate_cw_ccw){
          digitalWriteEnhanced(rotate_cw_ccw, ROTATE_PIN_INACTIVE_VALUE);
        }        
        if (rotate_cw_freq) {
          noTone(rotate_cw_freq);
        }

        #ifdef FEATURE_STEPPER_MOTOR
        if (az_stepper_motor_pulse) {
          set_az_stepper_freq(0);
          digitalWriteEnhanced(az_stepper_motor_pulse,HIGH);
        }      
        #endif //FEATURE_STEPPER_MOTOR             
      }
      break;
    case CCW:
      #ifdef DEBUG_ROTATOR
      if (debug_mode) {
        control_port->print(F("CCW ")); control_port->flush();
      }
        #endif // DEBUG_ROTATOR
      if (rotation_action == ACTIVATE) {
          #ifdef DEBUG_ROTATOR
        if (debug_mode) {
          control_port->println(F("ACTIVATE")); control_port->flush();
        }
          #endif // DEBUG_ROTATOR
        brake_release(AZ, BRAKE_RELEASE_ON);
        if (az_slowstart_active) {
          if (rotate_cw_pwm) {
            analogWriteEnhanced(rotate_cw_pwm, 0); digitalWriteEnhanced(rotate_cw_pwm, LOW);
          }
          if (rotate_ccw_pwm) {
            analogWriteEnhanced(rotate_ccw_pwm, 0);
          }
          if (rotate_cw_ccw_pwm) {
            analogWriteEnhanced(rotate_cw_ccw_pwm, 0);
          }
          if (rotate_cw_freq) {
            noTone(rotate_cw_freq);
          }
          if (rotate_ccw_freq) {
            noTone(rotate_ccw_freq);
          } 
          #ifdef FEATURE_STEPPER_MOTOR
          if (az_stepper_motor_pulse) {
            set_az_stepper_freq(0);
            digitalWriteEnhanced(az_stepper_motor_pulse,LOW);
          }      
          #endif //FEATURE_STEPPER_MOTOR                
        } else {
          if (rotate_cw_pwm) {
            analogWriteEnhanced(rotate_cw_pwm, 0); digitalWriteEnhanced(rotate_cw_pwm, LOW);
          }
          if (rotate_ccw_pwm) {
            analogWriteEnhanced(rotate_ccw_pwm, normal_az_speed_voltage);
          }
          if (rotate_cw_ccw_pwm) {
            analogWriteEnhanced(rotate_cw_ccw_pwm, normal_az_speed_voltage);
          }
          if (rotate_cw_freq) {
            noTone(rotate_cw_freq);
          }
          if (rotate_ccw_freq) {
            tone(rotate_ccw_freq, map(normal_az_speed_voltage, 0, 255, AZ_VARIABLE_FREQ_OUTPUT_LOW, AZ_VARIABLE_FREQ_OUTPUT_HIGH));
          }  
          #ifdef FEATURE_STEPPER_MOTOR
          if (az_stepper_motor_pulse) {
            set_az_stepper_freq(map(normal_az_speed_voltage, 0, 255, AZ_VARIABLE_FREQ_OUTPUT_LOW, AZ_VARIABLE_FREQ_OUTPUT_HIGH));
          }
          #endif //FEATURE_STEPPER_MOTOR 
        }
        if (rotate_cw) {
          digitalWriteEnhanced(rotate_cw, ROTATE_PIN_INACTIVE_VALUE);
        }
        if (rotate_ccw) {
          digitalWriteEnhanced(rotate_ccw, ROTATE_PIN_ACTIVE_VALUE);
        }
        if (rotate_cw_ccw){
          digitalWriteEnhanced(rotate_cw_ccw, ROTATE_PIN_ACTIVE_VALUE);
        }      
        /* 
        #ifdef FEATURE_STEPPER_MOTOR
        if (az_stepper_motor_direction){
          if (configuration.az_stepper_motor_last_direction != STEPPER_CCW){
            if (configuration.az_stepper_motor_last_pin_state == LOW){
              digitalWriteEnhanced(az_stepper_motor_direction,HIGH);
              configuration.az_stepper_motor_last_pin_state = HIGH;
            } else {
              digitalWriteEnhanced(az_stepper_motor_direction,LOW);
              configuration.az_stepper_motor_last_pin_state = LOW;             
            }
            configuration.az_stepper_motor_last_direction = STEPPER_CCW;
            configuration_dirty = 1;
          }
        }
        #endif //FEATURE_STEPPER_MOTOR
        */
        #ifdef DEBUG_ROTATOR
        if (debug_mode) {
          control_port->print(F("rotator: normal_az_speed_voltage:"));
          control_port->println(normal_az_speed_voltage);
          control_port->flush();
        }
        #endif // DEBUG_ROTATOR
      } else {
        #ifdef DEBUG_ROTATOR
        if (debug_mode) {
          control_port->println(F("DEACTIVATE")); control_port->flush();
        }
              #endif // DEBUG_ROTATOR
        if (rotate_ccw_pwm) {
          analogWriteEnhanced(rotate_ccw_pwm, 0); digitalWriteEnhanced(rotate_ccw_pwm, LOW);
        }
        if (rotate_ccw) {
          digitalWriteEnhanced(rotate_ccw, ROTATE_PIN_INACTIVE_VALUE);
        }
        if (rotate_ccw_freq) {
          noTone(rotate_ccw_freq);
        }
      }
      break;

    #ifdef FEATURE_ELEVATION_CONTROL


    case UP:
    #ifdef DEBUG_ROTATOR
      if (debug_mode) {
        control_port->print(F("ROTATION_UP ")); control_port->flush();
      }
    #endif // DEBUG_ROTATOR
      if (rotation_action == ACTIVATE) {
      #ifdef DEBUG_ROTATOR
        if (debug_mode) {
          control_port->println(F("ACTIVATE")); control_port->flush();
        }
      #endif // DEBUG_ROTATOR
        brake_release(EL, BRAKE_RELEASE_ON);
        if (el_slowstart_active) {
          if (rotate_up_pwm) {
            analogWriteEnhanced(rotate_up_pwm, 0);
          }
          if (rotate_down_pwm) {
            analogWriteEnhanced(rotate_down_pwm, 0); digitalWriteEnhanced(rotate_down_pwm, LOW);
          }
          if (rotate_up_down_pwm) {
            analogWriteEnhanced(rotate_up_down_pwm, 0);
          }
          if (rotate_up_freq) {
            noTone(rotate_up_freq);
          }
          if (rotate_down_freq) {
            noTone(rotate_down_freq);
          }
          #ifdef FEATURE_STEPPER_MOTOR
          if (el_stepper_motor_pulse) {
            set_el_stepper_freq(0);
            digitalWriteEnhanced(el_stepper_motor_pulse,LOW);
          }      
          #endif //FEATURE_STEPPER_MOTOR    
        } else {
          if (rotate_up_pwm) {
            analogWriteEnhanced(rotate_up_pwm, normal_el_speed_voltage);
          }
          if (rotate_down_pwm) {
            analogWriteEnhanced(rotate_down_pwm, 0); digitalWriteEnhanced(rotate_down_pwm, LOW);
          }
          if (rotate_up_down_pwm) {
            analogWriteEnhanced(rotate_up_down_pwm, normal_el_speed_voltage);
          }
          if (rotate_up_freq) {
            tone(rotate_up_freq, map(normal_el_speed_voltage, 0, 255, EL_VARIABLE_FREQ_OUTPUT_LOW, EL_VARIABLE_FREQ_OUTPUT_HIGH));
          }
          #ifdef FEATURE_STEPPER_MOTOR
          if (el_stepper_motor_pulse) {
            set_el_stepper_freq(map(normal_el_speed_voltage, 0, 255, EL_VARIABLE_FREQ_OUTPUT_LOW, EL_VARIABLE_FREQ_OUTPUT_HIGH));
          }
          #endif //FEATURE_STEPPER_MOTOR  
          if (rotate_down_freq) {
            noTone(rotate_down_freq);
          }
        }
        if (rotate_up) {
          digitalWriteEnhanced(rotate_up, ROTATE_PIN_ACTIVE_VALUE);
        }
        if (rotate_down) {
          digitalWriteEnhanced(rotate_down, ROTATE_PIN_INACTIVE_VALUE);
        }
        if (rotate_up_or_down) {
          digitalWriteEnhanced(rotate_up_or_down, ROTATE_PIN_ACTIVE_VALUE);
        } 
        /*
        #ifdef FEATURE_STEPPER_MOTOR
        if (el_stepper_motor_direction){
          if (configuration.el_stepper_motor_last_direction != STEPPER_UP){
             if (configuration.el_stepper_motor_last_pin_state == LOW){
               digitalWriteEnhanced(el_stepper_motor_direction,HIGH);
               configuration.el_stepper_motor_last_pin_state = HIGH;
             } else {
               digitalWriteEnhanced(el_stepper_motor_direction,LOW);
               configuration.el_stepper_motor_last_pin_state = LOW;             
             }
             configuration.el_stepper_motor_last_direction = STEPPER_UP;
             configuration_dirty = 1;
          }
        }
        #endif //FEATURE_STEPPER_MOTOR  
        */         
      } else {
      #ifdef DEBUG_ROTATOR
        if (debug_mode) {
          control_port->println(F("DEACTIVATE")); control_port->flush();
        }
      #endif // DEBUG_ROTATOR
        if (rotate_up) {
          digitalWriteEnhanced(rotate_up, ROTATE_PIN_INACTIVE_VALUE);
        }
        if (rotate_up_pwm) {
          analogWriteEnhanced(rotate_up_pwm, 0); digitalWriteEnhanced(rotate_up_pwm, LOW);
        }
        if (rotate_up_down_pwm) {
          analogWriteEnhanced(rotate_up_down_pwm, 0);
        }
        if (rotate_up_freq) {
          noTone(rotate_up_freq);
        }
        if (rotate_up_or_down) {
          digitalWriteEnhanced(rotate_up_or_down, ROTATE_PIN_INACTIVE_VALUE);
        }   
        #ifdef FEATURE_STEPPER_MOTOR
        if (el_stepper_motor_pulse) {
          set_el_stepper_freq(0);
          digitalWriteEnhanced(el_stepper_motor_pulse,HIGH);
        }      
        #endif //FEATURE_STEPPER_MOTOR   
      }
      break;

    case DOWN:
      #ifdef DEBUG_ROTATOR
      if (debug_mode) {
        control_port->print(F("ROTATION_DOWN ")); control_port->flush();
      }
      #endif // DEBUG_ROTATOR
      if (rotation_action == ACTIVATE) {
        #ifdef DEBUG_ROTATOR
        if (debug_mode) {
          control_port->println(F("ACTIVATE")); control_port->flush();
        }
        #endif // DEBUG_ROTATOR
        brake_release(EL, BRAKE_RELEASE_ON);
        if (el_slowstart_active) {
          if (rotate_down_pwm) {
            analogWriteEnhanced(rotate_down_pwm, 0);
          }
          if (rotate_up_pwm) {
            analogWriteEnhanced(rotate_up_pwm, 0); digitalWriteEnhanced(rotate_up_pwm, LOW);
          }
          if (rotate_up_down_pwm) {
            analogWriteEnhanced(rotate_up_down_pwm, 0);
          }
          if (rotate_up_freq) {
            noTone(rotate_up_freq);
          }
          if (rotate_down_freq) {
            noTone(rotate_down_freq);
          }
          #ifdef FEATURE_STEPPER_MOTOR
          if (el_stepper_motor_pulse) {
            set_el_stepper_freq(0);
            digitalWriteEnhanced(el_stepper_motor_pulse,LOW);        
          }      
          #endif //FEATURE_STEPPER_MOTOR             
        } else {
          if (rotate_down_pwm) {
            analogWriteEnhanced(rotate_down_pwm, normal_el_speed_voltage);
          }
          if (rotate_up_pwm) {
            analogWriteEnhanced(rotate_up_pwm, 0); digitalWriteEnhanced(rotate_up_pwm, LOW);
          }
          if (rotate_up_down_pwm) {
            analogWriteEnhanced(rotate_up_down_pwm, normal_el_speed_voltage);
          }
          if (rotate_down_freq) {
            tone(rotate_down_freq, map(normal_el_speed_voltage, 0, 255, EL_VARIABLE_FREQ_OUTPUT_LOW, EL_VARIABLE_FREQ_OUTPUT_HIGH));
          }
          if (rotate_up_freq) {
            noTone(rotate_up_freq);
          }
          #ifdef FEATURE_STEPPER_MOTOR
          if (el_stepper_motor_pulse) {
            set_el_stepper_freq(map(normal_el_speed_voltage, 0, 255, EL_VARIABLE_FREQ_OUTPUT_LOW, EL_VARIABLE_FREQ_OUTPUT_HIGH));
            digitalWriteEnhanced(el_stepper_motor_pulse,LOW);           
          }      
          #endif //FEATURE_STEPPER_MOTOR             
        }
        if (rotate_up) {
          digitalWriteEnhanced(rotate_up, ROTATE_PIN_INACTIVE_VALUE);
        }
        if (rotate_down) {
          digitalWriteEnhanced(rotate_down, ROTATE_PIN_ACTIVE_VALUE);
        }
        if (rotate_up_or_down) {
          digitalWriteEnhanced(rotate_up_or_down, ROTATE_PIN_ACTIVE_VALUE);
        }      
        /*   
        #ifdef FEATURE_STEPPER_MOTOR
        if (el_stepper_motor_direction){
          if (configuration.el_stepper_motor_last_direction != STEPPER_DOWN){
             if (configuration.el_stepper_motor_last_pin_state == LOW){
               digitalWriteEnhanced(el_stepper_motor_direction,HIGH);
               configuration.el_stepper_motor_last_pin_state = HIGH;
             } else {
               digitalWriteEnhanced(el_stepper_motor_direction,LOW);
               configuration.el_stepper_motor_last_pin_state = LOW;          
             }
             configuration.el_stepper_motor_last_direction = STEPPER_DOWN;
             configuration_dirty = 1;
          }
        }
        #endif //FEATURE_STEPPER_MOTOR
        */  
      } else {
          #ifdef DEBUG_ROTATOR
        if (debug_mode) {
          control_port->println(F("DEACTIVATE")); control_port->flush();
        }
          #endif // DEBUG_ROTATOR
        if (rotate_down) {
          digitalWriteEnhanced(rotate_down, ROTATE_PIN_INACTIVE_VALUE);
        }
        if (rotate_down_pwm) {
          analogWriteEnhanced(rotate_down_pwm, 0); digitalWriteEnhanced(rotate_down_pwm, LOW);
        }
        if (rotate_up_down_pwm) {
          analogWriteEnhanced(rotate_up_down_pwm, 0);
        }
        if (rotate_down_freq) {
          noTone(rotate_down_freq);
        }
        if (rotate_up_or_down) {
          digitalWriteEnhanced(rotate_up_or_down, ROTATE_PIN_INACTIVE_VALUE);
        }        
        #ifdef FEATURE_STEPPER_MOTOR
        if (el_stepper_motor_pulse) {
          set_el_stepper_freq(0);
          digitalWriteEnhanced(el_stepper_motor_pulse,HIGH);          
        }      
        #endif //FEATURE_STEPPER_MOTOR 
      }
      break;
          #endif // FEATURE_ELEVATION_CONTROL
  } /* switch */

  #ifdef DEBUG_ROTATOR
  if (debug_mode) {
    control_port->println(F("rotator: exiting"));
    control_port->flush();
  }
  #endif // DEBUG_ROTATOR
} /* rotator */

// --------------------------------------------------------------
void initialize_interrupts(){

  #ifdef FEATURE_AZ_POSITION_PULSE_INPUT
  attachInterrupt(AZ_POSITION_PULSE_PIN_INTERRUPT, az_position_pulse_interrupt_handler, FALLING);
  #endif // FEATURE_AZ_POSITION_PULSE_INPUT

  #ifdef FEATURE_EL_POSITION_PULSE_INPUT
  attachInterrupt(EL_POSITION_PULSE_PIN_INTERRUPT, el_position_pulse_interrupt_handler, FALLING);
  #endif // FEATURE_EL_POSITION_PULSE_INPUT

  #ifdef FEATURE_STEPPER_MOTOR
  Timer5.initialize(250);  // 250 us = 4 khz rate
  Timer5.attachInterrupt(service_stepper_motor_pulse_pins);
  #endif //FEATURE_STEPPER_MOTOR


}

// --------------------------------------------------------------

void initialize_pins(){

  #ifdef reset_pin
  pinMode(reset_pin, OUTPUT);
  digitalWrite(reset_pin, LOW);
  #endif //reset_pin

  if (serial_led) {
    pinModeEnhanced(serial_led, OUTPUT);
  }

  if (overlap_led) {
    pinModeEnhanced(overlap_led, OUTPUT);
  }

  if (brake_az) {
    pinModeEnhanced(brake_az, OUTPUT);
    digitalWriteEnhanced(brake_az, BRAKE_INACTIVE_STATE);
  }

  if (az_speed_pot) {
    pinModeEnhanced(az_speed_pot, INPUT);
    digitalWriteEnhanced(az_speed_pot, LOW);
  }

  if (az_preset_pot) {
    pinModeEnhanced(az_preset_pot, INPUT);
    digitalWriteEnhanced(az_preset_pot, LOW);
  }

  if (preset_start_button) {
    pinModeEnhanced(preset_start_button, INPUT);
    digitalWriteEnhanced(preset_start_button, HIGH);
  }

  if (button_stop) {
    pinModeEnhanced(button_stop, INPUT);
    digitalWriteEnhanced(button_stop, HIGH);
  }

  #ifdef FEATURE_ELEVATION_CONTROL
  if (brake_el) {
    pinModeEnhanced(brake_el, OUTPUT);
    digitalWriteEnhanced(brake_el, BRAKE_INACTIVE_STATE);
  }
  #endif // FEATURE_ELEVATION_CONTROL

  if (rotate_cw) {
    pinModeEnhanced(rotate_cw, OUTPUT);
  }
  if (rotate_ccw) {
    pinModeEnhanced(rotate_ccw, OUTPUT);
  }
  if (rotate_cw_pwm) {
    pinModeEnhanced(rotate_cw_pwm, OUTPUT);
  }
  if (rotate_ccw_pwm) {
    pinModeEnhanced(rotate_ccw_pwm, OUTPUT);
  }
  if (rotate_cw_ccw_pwm) {
    pinModeEnhanced(rotate_cw_ccw_pwm, OUTPUT);
  }
  if (rotate_cw_freq) {
    pinModeEnhanced(rotate_cw_freq, OUTPUT);
  }
  if (rotate_ccw_freq) {
    pinModeEnhanced(rotate_ccw_freq, OUTPUT);
  }

  if (rotate_cw_ccw) {
    pinModeEnhanced(rotate_cw_ccw, OUTPUT);
  }


  rotator(DEACTIVATE, CW);
  rotator(DEACTIVATE, CCW);

  #ifndef FEATURE_AZ_POSITION_HMC5883L
  pinModeEnhanced(rotator_analog_az, INPUT);
  #endif

  if (button_cw) {
    pinModeEnhanced(button_cw, INPUT);
    digitalWriteEnhanced(button_cw, HIGH);
  }
  if (button_ccw) {
    pinModeEnhanced(button_ccw, INPUT);
    digitalWriteEnhanced(button_ccw, HIGH);
  }

  normal_az_speed_voltage = PWM_SPEED_VOLTAGE_X4;
  current_az_speed_voltage = PWM_SPEED_VOLTAGE_X4;

  #ifdef FEATURE_ELEVATION_CONTROL
  normal_el_speed_voltage = PWM_SPEED_VOLTAGE_X4;
  current_el_speed_voltage = PWM_SPEED_VOLTAGE_X4;
  #endif // FEATURE_ELEVATION_CONTROL

  if (azimuth_speed_voltage) {                 // if azimuth_speed_voltage pin is configured, set it up for PWM output
    analogWriteEnhanced(azimuth_speed_voltage, PWM_SPEED_VOLTAGE_X4);
  }


  #ifdef FEATURE_ELEVATION_CONTROL
  pinModeEnhanced(rotate_up, OUTPUT);
  pinModeEnhanced(rotate_down, OUTPUT);
  if (rotate_up_or_down) {
    pinModeEnhanced(rotate_up_or_down, OUTPUT);
  }
  if (rotate_up_pwm) {
    pinModeEnhanced(rotate_up_pwm, OUTPUT);
  }
  if (rotate_down_pwm) {
    pinModeEnhanced(rotate_down_pwm, OUTPUT);
  }
  if (rotate_up_down_pwm) {
    pinModeEnhanced(rotate_up_down_pwm, OUTPUT);
  }
  if (rotate_up_freq) {
    pinModeEnhanced(rotate_up_freq, OUTPUT);
  }
  if (rotate_down_freq) {
    pinModeEnhanced(rotate_down_freq, OUTPUT);
  }
  rotator(DEACTIVATE, UP);
  rotator(DEACTIVATE, DOWN);
  #ifdef FEATURE_EL_POSITION_POTENTIOMETER
  pinModeEnhanced(rotator_analog_el, INPUT);
  #endif // FEATURE_EL_POSITION_POTENTIOMETER
  if (button_up) {
    pinModeEnhanced(button_up, INPUT);
    digitalWriteEnhanced(button_up, HIGH);
  }
  if (button_down) {
    pinModeEnhanced(button_down, INPUT);
    digitalWriteEnhanced(button_down, HIGH);
  }

  if (elevation_speed_voltage) {                 // if elevation_speed_voltage pin is configured, set it up for PWM output
    analogWriteEnhanced(elevation_speed_voltage, PWM_SPEED_VOLTAGE_X4);
    normal_el_speed_voltage = PWM_SPEED_VOLTAGE_X4;
    current_el_speed_voltage = PWM_SPEED_VOLTAGE_X4;
  }

  read_elevation(0);
  #endif // FEATURE_ELEVATION_CONTROL

  #ifdef FEATURE_AZ_POSITION_PULSE_INPUT
  if (az_position_pulse_pin) {
    pinModeEnhanced(az_position_pulse_pin, INPUT);
    #ifdef OPTION_POSITION_PULSE_INPUT_PULLUPS
    digitalWriteEnhanced(az_position_pulse_pin, HIGH);
    #endif // OPTION_POSITION_PULSE_INPUT_PULLUPS
  }
  #endif // FEATURE_AZ_POSITION_PULSE_INPUT


  #ifdef FEATURE_EL_POSITION_PULSE_INPUT
  if (el_position_pulse_pin) {
    pinModeEnhanced(el_position_pulse_pin, INPUT);
    #ifdef OPTION_POSITION_PULSE_INPUT_PULLUPS
    digitalWriteEnhanced(el_position_pulse_pin, HIGH);
    #endif // OPTION_POSITION_PULSE_INPUT_PULLUPS
  }
  #endif // FEATURE_EL_POSITION_PULSE_INPUT

  #ifdef FEATURE_PARK
  if (button_park) {
    pinModeEnhanced(button_park, INPUT);
    digitalWriteEnhanced(button_park, HIGH);
  }
  #endif // FEATURE_PARK

  #ifdef FEATURE_ROTATION_INDICATOR_PIN
  if (rotation_indication_pin) {
    pinModeEnhanced(rotation_indication_pin, OUTPUT);
    digitalWriteEnhanced(rotation_indication_pin, ROTATION_INDICATOR_PIN_INACTIVE_STATE);
  }
  #endif // FEATURE_ROTATION_INDICATOR_PIN

  #ifdef FEATURE_PARK
  if (park_in_progress_pin) {
    pinModeEnhanced(park_in_progress_pin, OUTPUT);
    digitalWriteEnhanced(park_in_progress_pin, LOW);
  }
  if (parked_pin) {
    pinModeEnhanced(parked_pin, OUTPUT);
    digitalWriteEnhanced(parked_pin, LOW);
  }
  #endif // FEATURE_PARK

  if (blink_led) {
    pinModeEnhanced(blink_led, OUTPUT);
  }

  if (heading_reading_inhibit_pin) {
    pinModeEnhanced(heading_reading_inhibit_pin, INPUT);
  }

  #ifdef FEATURE_LIMIT_SENSE
  if (az_limit_sense_pin) {
    pinModeEnhanced(az_limit_sense_pin, INPUT);
    digitalWriteEnhanced(az_limit_sense_pin, HIGH);
  }
  #ifdef FEATURE_ELEVATION_CONTROL
  if (el_limit_sense_pin) {
    pinModeEnhanced(el_limit_sense_pin, INPUT);
    digitalWriteEnhanced(el_limit_sense_pin, HIGH);
  }
  #endif // FEATURE_ELEVATION_CONTROL
  #endif // FEATURE_LIMIT_SENSE

  #ifdef FEATURE_MOON_TRACKING
  if (moon_tracking_active_pin) {
    pinModeEnhanced(moon_tracking_active_pin, OUTPUT);
    digitalWriteEnhanced(moon_tracking_active_pin, LOW);
  }
  if (moon_tracking_activate_line) {
    pinModeEnhanced(moon_tracking_activate_line, INPUT);
    digitalWriteEnhanced(moon_tracking_activate_line, HIGH);
  }
  if (moon_tracking_button) {
    pinModeEnhanced(moon_tracking_button, INPUT);
    digitalWriteEnhanced(moon_tracking_button, HIGH);
  }
  #endif // FEATURE_MOON_TRACKING


  #ifdef FEATURE_SUN_TRACKING
  if (sun_tracking_active_pin) {
    pinModeEnhanced(sun_tracking_active_pin, OUTPUT);
    digitalWriteEnhanced(sun_tracking_active_pin, LOW);
  }
  if (sun_tracking_activate_line) {
    pinModeEnhanced(sun_tracking_activate_line, INPUT);
    digitalWriteEnhanced(sun_tracking_activate_line, HIGH);
  }
  if (sun_tracking_button) {
    pinModeEnhanced(sun_tracking_button, INPUT);
    digitalWriteEnhanced(sun_tracking_button, HIGH);
  }
  #endif // FEATURE_SUN_TRACKING
  
  
  #ifdef FEATURE_GPS
  if (gps_sync) {
    pinModeEnhanced(gps_sync, OUTPUT);
    digitalWriteEnhanced(gps_sync, LOW);
  }
  #endif //FEATURE_GPS

  #ifdef FEATURE_POWER_SWITCH
  pinModeEnhanced(power_switch, OUTPUT);
  digitalWriteEnhanced(power_switch, HIGH);
  #endif //FEATURE_POWER_SWITCH

  #ifdef FEATURE_STEPPER_MOTOR
  if (az_stepper_motor_pulse){
    pinModeEnhanced(az_stepper_motor_pulse, OUTPUT);
    digitalWriteEnhanced(az_stepper_motor_pulse, HIGH);
  }
  /*
  if (az_stepper_motor_direction){
    pinModeEnhanced(az_stepper_motor_direction, OUTPUT);
    digitalWriteEnhanced(az_stepper_motor_direction, configuration.az_stepper_motor_last_pin_state);
  }
  */

  #ifdef FEATURE_ELEVATION_CONTROL
  if (el_stepper_motor_pulse){
    pinModeEnhanced(el_stepper_motor_pulse, OUTPUT);
    digitalWriteEnhanced(el_stepper_motor_pulse, HIGH);
  }
  /*
  if (el_stepper_motor_direction){
    pinModeEnhanced(el_stepper_motor_direction, OUTPUT);
    digitalWriteEnhanced(el_stepper_motor_direction, configuration.el_stepper_motor_last_pin_state);
  }
  */
  #endif //FEATURE_ELEVATION_CONTROL
  #endif //FEATURE_STEPPER_MOTOR

  #ifdef FEATURE_EL_POSITION_MEMSIC_2125
  pinModeEnhanced(pin_memsic_2125_x, INPUT);
  pinModeEnhanced(pin_memsic_2125_y, INPUT);
  #endif //FEATURE_EL_POSITION_MEMSIC_2125

  #ifdef FEATURE_ANALOG_OUTPUT_PINS
  pinModeEnhanced(pin_analog_az_out, OUTPUT);
  digitalWriteEnhanced(pin_analog_az_out, LOW);
  #ifdef FEATURE_ELEVATION_CONTROL
  pinModeEnhanced(pin_analog_el_out, OUTPUT);
  digitalWriteEnhanced(pin_analog_el_out, LOW);
  #endif //FEATURE_ELEVATION_CONTROL
  #endif //FEATURE_ANALOG_OUTPUT_PINS

  #ifdef FEATURE_SUN_PUSHBUTTON_AZ_EL_CALIBRATION
  pinModeEnhanced(pin_sun_pushbutton_calibration, INPUT);
  digitalWriteEnhanced(pin_sun_pushbutton_calibration, HIGH);
  #endif //FEATURE_SUN_PUSHBUTTON_AZ_EL_CALIBRATION

  #ifdef FEATURE_MOON_PUSHBUTTON_AZ_EL_CALIBRATION
  pinModeEnhanced(pin_moon_pushbutton_calibration, INPUT);
  digitalWriteEnhanced(pin_moon_pushbutton_calibration, HIGH);
  #endif //FEATURE_MOON_PUSHBUTTON_AZ_EL_CALIBRATION

} /* initialize_pins */

// --------------------------------------------------------------

void initialize_serial(){

  #if defined(FEATURE_REMOTE_UNIT_SLAVE) || defined(FEATURE_YAESU_EMULATION) || defined(FEATURE_EASYCOM_EMULATION) || defined(FEATURE_CLOCK)
  control_port = CONTROL_PORT_MAPPED_TO;
  control_port->begin(CONTROL_PORT_BAUD_RATE);
  //added Craig Benson UNSW@ADFA 2015-05-01 for debugging
 // control_port->println("in initialize_serial() \n");
  //end added 2015-05-01
  #endif

  #ifdef FEATURE_REMOTE_UNIT_SLAVE
  control_port->print(F("CS"));
  control_port->println(CODE_VERSION);
  #endif // FEATURE_REMOTE_UNIT_SLAVE

  #if defined(FEATURE_MASTER_WITH_SERIAL_SLAVE)
  remote_unit_port = REMOTE_PORT_MAPPED_TO;
  remote_unit_port->begin(REMOTE_UNIT_PORT_BAUD_RATE);
  #endif

  #ifdef FEATURE_GPS
  gps_port = GPS_PORT_MAPPED_TO;
  gps_port->begin(GPS_PORT_BAUD_RATE);
  #ifdef GPS_MIRROR_PORT
  gps_mirror_port = GPS_MIRROR_PORT;
  gps_mirror_port->begin(GPS_MIRROR_PORT_BAUD_RATE);
  #endif //GPS_MIRROR_PORT
  #endif //FEATURE_GPS

} /* initialize_serial */

// --------------------------------------------------------------

#ifdef FEATURE_LCD_DISPLAY
void initialize_display(){

  byte start_row = 0;


  lcd.begin(LCD_COLUMNS, LCD_ROWS);

  #ifdef FEATURE_YOURDUINO_I2C_LCD
  lcd.setBacklightPin(BACKLIGHT_PIN, POSITIVE);
  lcd.setBacklight(I2C_LCD_COLOR);
  #endif // FEATURE_YOURDUINO_I2C_LCD

  #ifdef FEATURE_ADAFRUIT_I2C_LCD
  lcd.setBacklight(I2C_LCD_COLOR);
  #endif // FEATURE_ADAFRUIT_I2C_LCD

  #ifdef OPTION_DISPLAY_VERSION_ON_STARTUP
     if (LCD_ROWS == 4){start_row = 0;}
  #else 
     if (LCD_ROWS == 4){start_row = 1;}
  #endif  

  lcd.setCursor(((LCD_COLUMNS - 4) / 2), start_row);
  lcd.print("\x4B\x33\x4E\x47");                           
  lcd.setCursor(((LCD_COLUMNS - 16) / 2), start_row + 1);
  lcd.print("\x52\x6F\x74\x6F\x72\x20\x43\x6F\x6E\x74\x72\x6F\x6C\x6C\x65\x72");
  #ifdef OPTION_DISPLAY_VERSION_ON_STARTUP  //code provided by Paolo, IT9IPQ
  if (LCD_ROWS == 4) {
    lcd.setCursor(0, start_row + 3);
  }
  if (LCD_ROWS == 2) {
    lcd.setCursor(0, 1);
  }
  lcd.print("Ver: "); lcd.print(CODE_VERSION);
  #endif //OPTION_DISPLAY_VERSION_ON_STARTUP
  last_lcd_update = millis();

} /* initialize_display */
#endif /* ifdef FEATURE_LCD_DISPLAY */

// --------------------------------------------------------------

void initialize_peripherals(){

  #ifdef FEATURE_WIRE_SUPPORT
  Wire.begin();
  #endif

  #ifdef FEATURE_AZ_POSITION_HMC5883L
  compass = HMC5883L();
  int error;
  error = compass.SetScale(1.3); // Set the scale of the compass.
  #ifndef OPTION_DISABLE_HMC5883L_ERROR_CHECKING
  if (error != 0) {
    #if defined(FEATURE_REMOTE_UNIT_SLAVE) || defined(FEATURE_YAESU_EMULATION) || defined(FEATURE_EASYCOM_EMULATION)
    control_port->print(F("setup: compass error:"));
    control_port->println(compass.GetErrorText(error)); // check if there is an error, and print if so
    #endif
  }
  #endif //OPTION_DISABLE_HMC5883L_ERROR_CHECKING
  error = compass.SetMeasurementMode(Measurement_Continuous); // Set the measurement mode to Continuous
  #ifndef OPTION_DISABLE_HMC5883L_ERROR_CHECKING
  if (error != 0) {
    #if defined(FEATURE_REMOTE_UNIT_SLAVE) || defined(FEATURE_YAESU_EMULATION) || defined(FEATURE_EASYCOM_EMULATION)
    control_port->print(F("setup: compass error:"));
    control_port->println(compass.GetErrorText(error)); // check if there is an error, and print if so
    #endif
  }
  #endif //OPTION_DISABLE_HMC5883L_ERROR_CHECKING
  #endif // FEATURE_AZ_POSITION_HMC5883L

  #ifdef FEATURE_EL_POSITION_ADXL345_USING_LOVE_ELECTRON_LIB
  accel = ADXL345();
  accel.SetRange(2, true);
  accel.EnableMeasurements();
  #endif // FEATURE_EL_POSITION_ADXL345_USING_LOVE_ELECTRON_LIB

  #ifdef FEATURE_EL_POSITION_ADXL345_USING_ADAFRUIT_LIB
  accel.begin();
  #endif // FEATURE_EL_POSITION_ADXL345_USING_ADAFRUIT_LIB

  #ifdef FEATURE_JOYSTICK_CONTROL
  pinModeEnhanced(pin_joystick_x, INPUT);
  pinModeEnhanced(pin_joystick_y, INPUT);
  #endif // FEATURE_JOYSTICK_CONTROL

  #if defined(FEATURE_EL_POSITION_ADAFRUIT_LSM303) || defined(FEATURE_AZ_POSITION_ADAFRUIT_LSM303)
  if (!lsm.begin()) {
    #if defined(FEATURE_REMOTE_UNIT_SLAVE) || defined(FEATURE_YAESU_EMULATION) || defined(FEATURE_EASYCOM_EMULATION)
    control_port->println(F("setup: LSM303 error"));
    #endif
  }
  #endif // FEATURE_EL_POSITION_ADAFRUIT_LSM303 || FEATURE_AZ_POSITION_ADAFRUIT_LSM303


  #if defined(FEATURE_AZ_POSITION_POLOLU_LSM303) || defined(FEATURE_EL_POSITION_POLOLU_LSM303)
  if (!compass.init()) {
    #if defined(FEATURE_REMOTE_UNIT_SLAVE) || defined(FEATURE_YAESU_EMULATION) || defined(FEATURE_EASYCOM_EMULATION)
    control_port->println(F("setup: LSM303 error"));
    #endif
  }
  compass.enableDefault();
  compass.m_min = (LSM303::vector<int16_t>) POLOLU_LSM_303_MIN_ARRAY;
  compass.m_max = (LSM303::vector<int16_t>) POLOLU_LSM_303_MAX_ARRAY;
  #endif //defined(FEATURE_AZ_POSITION_POLOLU_LSM303) || defined(FEATURE_EL_POSITION_POLOLU_LSM303)


  #ifdef FEATURE_AZ_POSITION_HH12_AS5045_SSI
  azimuth_hh12.initialize(az_hh12_clock_pin, az_hh12_cs_pin, az_hh12_data_pin);
  #endif // FEATURE_AZ_POSITION_HH12_AS5045_SSI

  #ifdef FEATURE_EL_POSITION_HH12_AS5045_SSI
  elevation_hh12.initialize(el_hh12_clock_pin, el_hh12_cs_pin, el_hh12_data_pin);
  #endif // FEATURE_EL_POSITION_HH12_AS5045_SSI

  #ifdef FEATURE_RTC_DS1307
  rtc.begin();
  #endif // FEATURE_RTC_DS1307

  #ifdef FEATURE_ETHERNET
  Ethernet.begin(mac, ip, gateway, subnet);
  ethernetserver0.begin();
  #endif //FEATURE_ETHERNET

} /* initialize_peripherals */


// --------------------------------------------------------------
void submit_request(byte axis, byte request, int parm, byte called_by){

  #ifdef DEBUG_SUBMIT_REQUEST
  debug_print("submit_request: ");
  debug_print_int(called_by);
  debug_print(" ");
  #endif // DEBUG_SUBMIT_REQUEST

  #ifdef FEATURE_PARK
  park_status = NOT_PARKED;
  #endif // FEATURE_PARK

  if (axis == AZ) {
    #ifdef DEBUG_SUBMIT_REQUEST
    debug_print("AZ "); 
    #endif // DEBUG_SUBMIT_REQUEST
    az_request = request;
    az_request_parm = parm;
    az_request_queue_state = IN_QUEUE;
  }

  #ifdef FEATURE_ELEVATION_CONTROL
  if (axis == EL) {
    #ifdef DEBUG_SUBMIT_REQUEST
    debug_print("EL ");
    #endif // DEBUG_SUBMIT_REQUEST
    el_request = request;
    el_request_parm = parm;
    el_request_queue_state = IN_QUEUE;
  }
  #endif // FEATURE_ELEVATION_CONTROL

  #ifdef DEBUG_SUBMIT_REQUEST
  switch(request){
    case 0: debug_print("REQUEST_STOP");break;
    case 1: debug_print("REQUEST_AZIMUTH");break;
    case 2: debug_print("REQUEST_AZIMUTH_RAW");break;
    case 3: debug_print("REQUEST_CW");break;
    case 4: debug_print("REQUEST_CCW");break;
    case 5: debug_print("REQUEST_UP");break;
    case 6: debug_print("REQUEST_DOWN");break;
    case 7: debug_print("REQUEST_ELEVATION");break;
    case 8: debug_print("REQUEST_KILL");break;
  }
  debug_print(" ");
  debug_print_int(parm);
  debug_println("");
  #endif // DEBUG_SUBMIT_REQUEST  

} /* submit_request */
// --------------------------------------------------------------
void service_rotation(){



  #if defined(FEATURE_AZ_POSITION_INCREMENTAL_ENCODER) || defined(FEATURE_EL_POSITION_INCREMENTAL_ENCODER)
  service_rotation_lock = 1;
  #endif

  static byte az_direction_change_flag = 0;
  static byte az_initial_slow_down_voltage = 0;

  #ifdef FEATURE_ELEVATION_CONTROL
  static byte el_direction_change_flag = 0;
  static byte el_initial_slow_down_voltage = 0;
  #endif // FEATURE_ELEVATION_CONTROL

  if (az_state == INITIALIZE_NORMAL_CW) {
    update_az_variable_outputs(normal_az_speed_voltage);
    rotator(ACTIVATE, CW);
    az_state = NORMAL_CW;
  }

  if (az_state == INITIALIZE_NORMAL_CCW) {
    update_az_variable_outputs(normal_az_speed_voltage);
    rotator(ACTIVATE, CCW);
    az_state = NORMAL_CCW;
  }

  if (az_state == INITIALIZE_SLOW_START_CW) {
    update_az_variable_outputs(AZ_SLOW_START_STARTING_PWM);
    rotator(ACTIVATE, CW);
    az_slowstart_start_time = millis();
    az_last_step_time = 0;
    az_slow_start_step = 0;
    az_state = SLOW_START_CW;
    #ifdef DEBUG_SERVICE_ROTATION
    debug_print("service_rotation: INITIALIZE_SLOW_START_CW -> SLOW_START_CW");
    #endif // DEBUG_SERVICE_ROTATION
  }

  if (az_state == INITIALIZE_SLOW_START_CCW) {
    update_az_variable_outputs(AZ_SLOW_START_STARTING_PWM);
    rotator(ACTIVATE, CCW);
    az_slowstart_start_time = millis();
    az_last_step_time = 0;
    az_slow_start_step = 0;
    az_state = SLOW_START_CCW;
    #ifdef DEBUG_SERVICE_ROTATION
    debug_print("service_rotation: INITIALIZE_SLOW_START_CCW -> SLOW_START_CCW");
    #endif // DEBUG_SERVICE_ROTATION
  }

  if (az_state == INITIALIZE_TIMED_SLOW_DOWN_CW) {
    az_direction_change_flag = 0;
    az_timed_slow_down_start_time = millis();
    az_last_step_time = millis();
    az_slow_down_step = AZ_SLOW_DOWN_STEPS - 1;
    az_state = TIMED_SLOW_DOWN_CW;
  }

  if (az_state == INITIALIZE_TIMED_SLOW_DOWN_CCW) {
    az_direction_change_flag = 0;
    az_timed_slow_down_start_time = millis();
    az_last_step_time = millis();
    az_slow_down_step = AZ_SLOW_DOWN_STEPS - 1;
    az_state = TIMED_SLOW_DOWN_CCW;
  }

  if (az_state == INITIALIZE_DIR_CHANGE_TO_CW) {
    az_direction_change_flag = 1;
    az_timed_slow_down_start_time = millis();
    az_last_step_time = millis();
    az_slow_down_step = AZ_SLOW_DOWN_STEPS - 1;
    az_state = TIMED_SLOW_DOWN_CCW;
  }

  if (az_state == INITIALIZE_DIR_CHANGE_TO_CCW) {
    az_direction_change_flag = 1;
    az_timed_slow_down_start_time = millis();
    az_last_step_time = millis();
    az_slow_down_step = AZ_SLOW_DOWN_STEPS - 1;
    az_state = TIMED_SLOW_DOWN_CW;
  }

  // slow start-------------------------------------------------------------------------------------------------
  if ((az_state == SLOW_START_CW) || (az_state == SLOW_START_CCW)) {
    if ((millis() - az_slowstart_start_time) >= AZ_SLOW_START_UP_TIME) {  // is it time to end slow start?
      #ifdef DEBUG_SERVICE_ROTATION
      debug_print("service_rotation: NORMAL_C");
      #endif // DEBUG_SERVICE_ROTATION
      if (az_state == SLOW_START_CW) {
        az_state = NORMAL_CW;
        #ifdef DEBUG_SERVICE_ROTATION
        debug_print("W");
        #endif // DEBUG_SERVICE_ROTATION
      } else {
        az_state = NORMAL_CCW;
        #ifdef DEBUG_SERVICE_ROTATION
        debug_print("CW");
        #endif // DEBUG_SERVICE_ROTATION
      }
      update_az_variable_outputs(normal_az_speed_voltage);
    } else {  // it's not time to end slow start yet, but let's check if it's time to step up the speed voltage
      if (((millis() - az_last_step_time) > (AZ_SLOW_START_UP_TIME / AZ_SLOW_START_STEPS)) && (normal_az_speed_voltage > AZ_SLOW_START_STARTING_PWM)) {
        #ifdef DEBUG_SERVICE_ROTATION
        debug_print("service_rotation: step up: ");
        debug_print_int(az_slow_start_step);
        debug_print(" pwm: ");
        debug_print_int((int)(AZ_SLOW_START_STARTING_PWM + ((normal_az_speed_voltage - AZ_SLOW_START_STARTING_PWM) * ((float)az_slow_start_step / (float)(AZ_SLOW_START_STEPS - 1)))));
        debug_println("");
        #endif // DEBUG_SERVICE_ROTATION
        update_az_variable_outputs((AZ_SLOW_START_STARTING_PWM + ((normal_az_speed_voltage - AZ_SLOW_START_STARTING_PWM) * ((float)az_slow_start_step / (float)(AZ_SLOW_START_STEPS - 1)))));
        az_last_step_time = millis();
        az_slow_start_step++;
      }
    }
  } // ((az_state == SLOW_START_CW) || (az_state == SLOW_START_CCW))


  // timed slow down ------------------------------------------------------------------------------------------------------
  if (((az_state == TIMED_SLOW_DOWN_CW) || (az_state == TIMED_SLOW_DOWN_CCW)) && ((millis() - az_last_step_time) >= (TIMED_SLOW_DOWN_TIME / AZ_SLOW_DOWN_STEPS))) {
    #ifdef DEBUG_SERVICE_ROTATION
    debug_print("service_rotation: TIMED_SLOW_DOWN step down: ");
    debug_print_int(az_slow_down_step);
    debug_print(" pwm: ");
    debug_print_int((int)(normal_az_speed_voltage * ((float)az_slow_down_step / (float)AZ_SLOW_DOWN_STEPS)));
    debug_println("");
    #endif // DEBUG_SERVICE_ROTATION
    update_az_variable_outputs((int)(normal_az_speed_voltage * ((float)az_slow_down_step / (float)AZ_SLOW_DOWN_STEPS)));
    az_last_step_time = millis();
    if (az_slow_down_step > 0) {az_slow_down_step--;}

    if (az_slow_down_step == 0) { // is it time to exit timed slow down?
      #ifdef DEBUG_SERVICE_ROTATION
      debug_print("service_rotation: TIMED_SLOW_DOWN->IDLE");
      #endif // DEBUG_SERVICE_ROTATION
      rotator(DEACTIVATE, CW);
      rotator(DEACTIVATE, CCW);        
      if (az_direction_change_flag) {
        if (az_state == TIMED_SLOW_DOWN_CW) {
          //rotator(ACTIVATE, CCW);
          if (az_slowstart_active) {
            az_state = INITIALIZE_SLOW_START_CCW;
          } else { az_state = NORMAL_CCW; };
          az_direction_change_flag = 0;
        }
        if (az_state == TIMED_SLOW_DOWN_CCW) {
          //rotator(ACTIVATE, CW);
          if (az_slowstart_active) {
            az_state = INITIALIZE_SLOW_START_CW;
          } else { az_state = NORMAL_CW; };
          az_direction_change_flag = 0;
        }
      } else {
        az_state = IDLE;
        az_request_queue_state = NONE;
      }
    }

  }  // ((az_state == TIMED_SLOW_DOWN_CW) || (az_state == TIMED_SLOW_DOWN_CCW))



  // slow down ---------------------------------------------------------------------------------------------------------------
  if ((az_state == SLOW_DOWN_CW) || (az_state == SLOW_DOWN_CCW)) {

    // is it time to do another step down?
    if (abs((target_raw_azimuth - raw_azimuth) / HEADING_MULTIPLIER) <= (((float)SLOW_DOWN_BEFORE_TARGET_AZ * ((float)az_slow_down_step / (float)AZ_SLOW_DOWN_STEPS)))) {
      #ifdef DEBUG_SERVICE_ROTATION
      debug_print("service_rotation: step down: ");
      debug_print_int(az_slow_down_step);
      debug_print(" pwm: ");
      debug_print_int((int)(AZ_SLOW_DOWN_PWM_STOP + ((az_initial_slow_down_voltage - AZ_SLOW_DOWN_PWM_STOP) * ((float)az_slow_down_step / (float)AZ_SLOW_DOWN_STEPS))));
      debug_println("");
      #endif // DEBUG_SERVICE_ROTATION
      update_az_variable_outputs((AZ_SLOW_DOWN_PWM_STOP + ((az_initial_slow_down_voltage - AZ_SLOW_DOWN_PWM_STOP) * ((float)az_slow_down_step / (float)AZ_SLOW_DOWN_STEPS))));
      if (az_slow_down_step > 0) {az_slow_down_step--;}
    }
  }  // ((az_state == SLOW_DOWN_CW) || (az_state == SLOW_DOWN_CCW))

  // normal -------------------------------------------------------------------------------------------------------------------
  // if slow down is enabled, see if we're ready to go into slowdown
  if (((az_state == NORMAL_CW) || (az_state == SLOW_START_CW) || (az_state == NORMAL_CCW) || (az_state == SLOW_START_CCW)) &&
      (az_request_queue_state == IN_PROGRESS_TO_TARGET) && az_slowdown_active && (abs((target_raw_azimuth - raw_azimuth) / HEADING_MULTIPLIER) <= SLOW_DOWN_BEFORE_TARGET_AZ)) {

    byte az_state_was = az_state;

    #ifdef DEBUG_SERVICE_ROTATION
    debug_print("service_rotation: SLOW_DOWN_C");
    #endif // DEBUG_SERVICE_ROTATION
    az_slow_down_step = AZ_SLOW_DOWN_STEPS - 1;
    if ((az_state == NORMAL_CW) || (az_state == SLOW_START_CW)) {
      az_state = SLOW_DOWN_CW;
      #ifdef DEBUG_SERVICE_ROTATION
      debug_print("W");
      #endif // DEBUG_SERVICE_ROTATION
    } else {
      az_state = SLOW_DOWN_CCW;
      #ifdef DEBUG_SERVICE_ROTATION
      debug_print("CW");
      #endif // DEBUG_SERVICE_ROTATION
    }
    
    if ((az_state_was == SLOW_START_CW) || (az_state_was == SLOW_START_CCW)){
      az_initial_slow_down_voltage = (AZ_INITIALLY_IN_SLOW_DOWN_PWM);
      update_az_variable_outputs(az_initial_slow_down_voltage);
      #ifdef DEBUG_SERVICE_ROTATION
      debug_print(" SLOW_START -> SLOW_DOWN az_initial_slow_down_voltage:");
      debug_print_int(az_initial_slow_down_voltage);
      debug_print(" ");
      #endif // DEBUG_SERVICE_ROTATION
    } else {
      if (AZ_SLOW_DOWN_PWM_START < current_az_speed_voltage) {
        update_az_variable_outputs(AZ_SLOW_DOWN_PWM_START);
        az_initial_slow_down_voltage = AZ_SLOW_DOWN_PWM_START;
      } else {
        az_initial_slow_down_voltage = current_az_speed_voltage;
      }
    }

  }

  // check rotation target --------------------------------------------------------------------------------------------------------
  if ((az_state != IDLE) && (az_request_queue_state == IN_PROGRESS_TO_TARGET) ) {
    if ((az_state == NORMAL_CW) || (az_state == SLOW_START_CW) || (az_state == SLOW_DOWN_CW)) {
      if ((abs(raw_azimuth - target_raw_azimuth) < (AZIMUTH_TOLERANCE * HEADING_MULTIPLIER)) || ((raw_azimuth > target_raw_azimuth) && ((raw_azimuth - target_raw_azimuth) < ((AZIMUTH_TOLERANCE + 5) * HEADING_MULTIPLIER)))) {
        delay(50);
        read_azimuth(0);
        if ((abs(raw_azimuth - target_raw_azimuth) < (AZIMUTH_TOLERANCE * HEADING_MULTIPLIER)) || ((raw_azimuth > target_raw_azimuth) && ((raw_azimuth - target_raw_azimuth) < ((AZIMUTH_TOLERANCE + 5) * HEADING_MULTIPLIER)))) {
          rotator(DEACTIVATE, CW);
          rotator(DEACTIVATE, CCW);
          az_state = IDLE;
          az_request_queue_state = NONE;
          #ifdef DEBUG_SERVICE_ROTATION
          debug_print("service_rotation: IDLE");
          #endif // DEBUG_SERVICE_ROTATION

          #if defined(FEATURE_PARK) && !defined(FEATURE_ELEVATION_CONTROL)
          if (park_status == PARK_INITIATED) {
            park_status = PARKED;
          }
          #endif // defined(FEATURE_PARK) && !defined(FEATURE_ELEVATION_CONTROL)

          #if defined(FEATURE_PARK) && defined(FEATURE_ELEVATION_CONTROL)
          if ((park_status == PARK_INITIATED) && (el_state == IDLE)) {
            park_status = PARKED;
          }
          #endif // defined(FEATURE_PARK) && !defined(FEATURE_ELEVATION_CONTROL)

        }
      }
    } else {
      if ((abs(raw_azimuth - target_raw_azimuth) < (AZIMUTH_TOLERANCE * HEADING_MULTIPLIER)) || ((raw_azimuth < target_raw_azimuth) && ((target_raw_azimuth - raw_azimuth) < ((AZIMUTH_TOLERANCE + 5) * HEADING_MULTIPLIER)))) {
        delay(50);
        read_azimuth(0);
        if ((abs(raw_azimuth - target_raw_azimuth) < (AZIMUTH_TOLERANCE * HEADING_MULTIPLIER)) || ((raw_azimuth < target_raw_azimuth) && ((target_raw_azimuth - raw_azimuth) < ((AZIMUTH_TOLERANCE + 5) * HEADING_MULTIPLIER)))) {
          rotator(DEACTIVATE, CW);
          rotator(DEACTIVATE, CCW);
          az_state = IDLE;
          az_request_queue_state = NONE;
          #ifdef DEBUG_SERVICE_ROTATION
          debug_print("service_rotation: IDLE");
          #endif // DEBUG_SERVICE_ROTATION

          #if defined(FEATURE_PARK) && !defined(FEATURE_ELEVATION_CONTROL)
          if (park_status == PARK_INITIATED) {
            park_status = PARKED;
          }
          #endif // defined(FEATURE_PARK) && !defined(FEATURE_ELEVATION_CONTROL)

          #if defined(FEATURE_PARK) && defined(FEATURE_ELEVATION_CONTROL)
          if ((park_status == PARK_INITIATED) && (el_state == IDLE)) {
            park_status = PARKED;
          }
          #endif // defined(FEATURE_PARK) && !defined(FEATURE_ELEVATION_CONTROL)

        }
      }
    }
  }



  #ifdef FEATURE_ELEVATION_CONTROL
  if (el_state == INITIALIZE_NORMAL_UP) {
    update_el_variable_outputs(normal_el_speed_voltage);
    rotator(ACTIVATE, UP);
    el_state = NORMAL_UP;
  }

  if (el_state == INITIALIZE_NORMAL_DOWN) {
    update_el_variable_outputs(normal_el_speed_voltage);
    rotator(ACTIVATE, DOWN);
    el_state = NORMAL_DOWN;
  }

  if (el_state == INITIALIZE_SLOW_START_UP) {
    update_el_variable_outputs(EL_SLOW_START_STARTING_PWM);
    rotator(ACTIVATE, UP);
    el_slowstart_start_time = millis();
    el_last_step_time = 0;
    el_slow_start_step = 0;
    el_state = SLOW_START_UP;
    #ifdef DEBUG_SERVICE_ROTATION
    debug_print("service_rotation: INITIALIZE_SLOW_START_UP -> SLOW_START_UP");
    #endif // DEBUG_SERVICE_ROTATION
  }

  if (el_state == INITIALIZE_SLOW_START_DOWN) {
    update_el_variable_outputs(EL_SLOW_START_STARTING_PWM);
    rotator(ACTIVATE, DOWN);
    el_slowstart_start_time = millis();
    el_last_step_time = 0;
    el_slow_start_step = 0;
    el_state = SLOW_START_DOWN;
    #ifdef DEBUG_SERVICE_ROTATION
    debug_print("service_rotation: INITIALIZE_SLOW_START_DOWN -> SLOW_START_DOWN");
    #endif // DEBUG_SERVICE_ROTATION
  }

  if (el_state == INITIALIZE_TIMED_SLOW_DOWN_UP) {
    el_direction_change_flag = 0;
    el_timed_slow_down_start_time = millis();
    el_last_step_time = millis();
    el_slow_down_step = EL_SLOW_DOWN_STEPS - 1;
    el_state = TIMED_SLOW_DOWN_UP;
  }

  if (el_state == INITIALIZE_TIMED_SLOW_DOWN_DOWN) {
    el_direction_change_flag = 0;
    el_timed_slow_down_start_time = millis();
    el_last_step_time = millis();
    el_slow_down_step = EL_SLOW_DOWN_STEPS - 1;
    el_state = TIMED_SLOW_DOWN_DOWN;
  }

  if (el_state == INITIALIZE_DIR_CHANGE_TO_UP) {
    el_direction_change_flag = 1;
    el_timed_slow_down_start_time = millis();
    el_last_step_time = millis();
    el_slow_down_step = EL_SLOW_DOWN_STEPS - 1;
    el_state = TIMED_SLOW_DOWN_DOWN;
  }

  if (el_state == INITIALIZE_DIR_CHANGE_TO_DOWN) {
    el_direction_change_flag = 1;
    el_timed_slow_down_start_time = millis();
    el_last_step_time = millis();
    el_slow_down_step = EL_SLOW_DOWN_STEPS - 1;
    el_state = TIMED_SLOW_DOWN_UP;
  }

  // slow start-------------------------------------------------------------------------------------------------
  if ((el_state == SLOW_START_UP) || (el_state == SLOW_START_DOWN)) {
    if ((millis() - el_slowstart_start_time) >= EL_SLOW_START_UP_TIME) {  // is it time to end slow start?
      #ifdef DEBUG_SERVICE_ROTATION
      debug_print("service_rotation: NORMAL_");
      #endif // DEBUG_SERVICE_ROTATION
      if (el_state == SLOW_START_UP) {
        el_state = NORMAL_UP;
        #ifdef DEBUG_SERVICE_ROTATION
        debug_print("UP");
        #endif // DEBUG_SERVICE_ROTATION
      } else {
        el_state = NORMAL_DOWN;
        #ifdef DEBUG_SERVICE_ROTATION
        debug_print("DOWN");
        #endif // DEBUG_SERVICE_ROTATION
      }
      update_el_variable_outputs(normal_el_speed_voltage);
    } else {  // it's not time to end slow start yet, but let's check if it's time to step up the speed voltage
      if (((millis() - el_last_step_time) > (EL_SLOW_START_UP_TIME / EL_SLOW_START_STEPS)) && (normal_el_speed_voltage > EL_SLOW_START_STARTING_PWM)) {
        #ifdef DEBUG_SERVICE_ROTATION
        debug_print("service_rotation: step up: ");
        debug_print_int(el_slow_start_step);
        debug_print(" pwm: ");
        debug_print_int((int)(EL_SLOW_START_STARTING_PWM + ((normal_el_speed_voltage - EL_SLOW_START_STARTING_PWM) * ((float)el_slow_start_step / (float)(EL_SLOW_START_STEPS - 1)))));
        debug_println("");
        #endif // DEBUG_SERVICE_ROTATION
        update_el_variable_outputs((EL_SLOW_START_STARTING_PWM + ((normal_el_speed_voltage - EL_SLOW_START_STARTING_PWM) * ((float)el_slow_start_step / (float)(EL_SLOW_START_STEPS - 1)))));
        el_last_step_time = millis();
        el_slow_start_step++;
      }
    }
  } // ((el_state == SLOW_START_UP) || (el_state == SLOW_START_DOWN))


  // timed slow down ------------------------------------------------------------------------------------------------------
  if (((el_state == TIMED_SLOW_DOWN_UP) || (el_state == TIMED_SLOW_DOWN_DOWN)) && ((millis() - el_last_step_time) >= (TIMED_SLOW_DOWN_TIME / EL_SLOW_DOWN_STEPS))) {
    #ifdef DEBUG_SERVICE_ROTATION
    debug_print("service_rotation: TIMED_SLOW_DOWN step down: ");
    debug_print_int(el_slow_down_step);
    debug_print(" pwm: ");
    debug_print_int((int)(normal_el_speed_voltage * ((float)el_slow_down_step / (float)EL_SLOW_DOWN_STEPS)));
    debug_println("");
    #endif // DEBUG_SERVICE_ROTATION
    update_el_variable_outputs((int)(normal_el_speed_voltage * ((float)el_slow_down_step / (float)EL_SLOW_DOWN_STEPS)));
    el_last_step_time = millis();
    if (el_slow_down_step > 0) {el_slow_down_step--;}

    if (el_slow_down_step == 0) { // is it time to exit timed slow down?
      #ifdef DEBUG_SERVICE_ROTATION
      debug_print("service_rotation: TIMED_SLOW_DOWN->IDLE");
      #endif // DEBUG_SERVICE_ROTATION
      rotator(DEACTIVATE, UP);
      rotator(DEACTIVATE, DOWN);
      if (el_direction_change_flag) {
        if (el_state == TIMED_SLOW_DOWN_UP) {
          if (el_slowstart_active) {
            el_state = INITIALIZE_SLOW_START_DOWN;
          } else { el_state = NORMAL_DOWN; };
          el_direction_change_flag = 0;
        }
        if (el_state == TIMED_SLOW_DOWN_DOWN) {
          if (el_slowstart_active) {
            el_state = INITIALIZE_SLOW_START_UP;
          } else { el_state = NORMAL_UP; };
          el_direction_change_flag = 0;
        }
      } else {
        el_state = IDLE;
        el_request_queue_state = NONE;
      }
    }

  }  // ((el_state == TIMED_SLOW_DOWN_UP) || (el_state == TIMED_SLOW_DOWN_DOWN))



  // slow down ---------------------------------------------------------------------------------------------------------------
  if ((el_state == SLOW_DOWN_UP) || (el_state == SLOW_DOWN_DOWN)) {
    // is it time to do another step down?
    if (abs((target_elevation - elevation) / HEADING_MULTIPLIER) <= (((float)SLOW_DOWN_BEFORE_TARGET_EL * ((float)el_slow_down_step / (float)EL_SLOW_DOWN_STEPS)))) {
      #ifdef DEBUG_SERVICE_ROTATION
      debug_print("service_rotation: step down: ");
      debug_print_int(el_slow_down_step);
      debug_print(" pwm: ");
      debug_print_int((int)(EL_SLOW_DOWN_PWM_STOP + ((el_initial_slow_down_voltage - EL_SLOW_DOWN_PWM_STOP) * ((float)el_slow_down_step / (float)EL_SLOW_DOWN_STEPS))));
      debug_println("");
      #endif // DEBUG_SERVICE_ROTATION
      update_el_variable_outputs((EL_SLOW_DOWN_PWM_STOP + ((el_initial_slow_down_voltage - EL_SLOW_DOWN_PWM_STOP) * ((float)el_slow_down_step / (float)EL_SLOW_DOWN_STEPS))));
      if (el_slow_down_step > 0) {el_slow_down_step--;}
    }
  }  // ((el_state == SLOW_DOWN_UP) || (el_state == SLOW_DOWN_DOWN))

  // normal -------------------------------------------------------------------------------------------------------------------
  // if slow down is enabled, see if we're ready to go into slowdown
  if (((el_state == NORMAL_UP) || (el_state == SLOW_START_UP) || (el_state == NORMAL_DOWN) || (el_state == SLOW_START_DOWN)) &&
      (el_request_queue_state == IN_PROGRESS_TO_TARGET) && el_slowdown_active && (abs((target_elevation - elevation) / HEADING_MULTIPLIER) <= SLOW_DOWN_BEFORE_TARGET_EL)) {
    
    byte el_state_was = el_state;


    #ifdef DEBUG_SERVICE_ROTATION
    debug_print("service_rotation: SLOW_DOWN_");
    #endif // DEBUG_SERVICE_ROTATION
    el_slow_down_step = EL_SLOW_DOWN_STEPS - 1;
    if ((el_state == NORMAL_UP) || (el_state == SLOW_START_UP)) {
      el_state = SLOW_DOWN_UP;
      #ifdef DEBUG_SERVICE_ROTATION
      debug_print("UP");
      #endif // DEBUG_SERVICE_ROTATION
    } else {
      el_state = SLOW_DOWN_DOWN;
      #ifdef DEBUG_SERVICE_ROTATION
      debug_print("DOWN");
      #endif // DEBUG_SERVICE_ROTATION
    }

    if ((el_state_was == SLOW_START_UP) || (el_state_was == SLOW_START_DOWN)){
      el_initial_slow_down_voltage = EL_INITIALLY_IN_SLOW_DOWN_PWM;
      update_el_variable_outputs(el_initial_slow_down_voltage);
      #ifdef DEBUG_SERVICE_ROTATION
      debug_print(" SLOW_START -> SLOW_DOWN el_initial_slow_down_voltage:");
      debug_print_int(el_initial_slow_down_voltage);
      debug_print(" ");
      #endif // DEBUG_SERVICE_ROTATION    

    } else {
      if (EL_SLOW_DOWN_PWM_START < current_el_speed_voltage) {
        update_el_variable_outputs(EL_SLOW_DOWN_PWM_START);
        el_initial_slow_down_voltage = EL_SLOW_DOWN_PWM_START;
      } else {
        el_initial_slow_down_voltage = current_el_speed_voltage;
      }
    }
  }

  // check rotation target --------------------------------------------------------------------------------------------------------
  if ((el_state != IDLE) && (el_request_queue_state == IN_PROGRESS_TO_TARGET) ) {
    read_elevation(0);
    if ((el_state == NORMAL_UP) || (el_state == SLOW_START_UP) || (el_state == SLOW_DOWN_UP)) {
      if ((abs(elevation - target_elevation) < (ELEVATION_TOLERANCE * HEADING_MULTIPLIER)) || ((elevation > target_elevation) && ((elevation - target_elevation) < ((ELEVATION_TOLERANCE + 5) * HEADING_MULTIPLIER)))) {
        #ifndef OPTION_NO_ELEVATION_CHECK_TARGET_DELAY
        delay(50);
        #endif //OPTION_NO_ELEVATION_CHECK_TARGET_DELAY
        read_elevation(0);
        if ((abs(elevation - target_elevation) < (ELEVATION_TOLERANCE * HEADING_MULTIPLIER)) || ((elevation > target_elevation) && ((elevation - target_elevation) < ((ELEVATION_TOLERANCE + 5) * HEADING_MULTIPLIER)))) {
          rotator(DEACTIVATE, UP);
          rotator(DEACTIVATE, DOWN);
          el_state = IDLE;
          el_request_queue_state = NONE;
          #ifdef DEBUG_SERVICE_ROTATION
          debug_print("service_rotation: IDLE");
          #endif // DEBUG_SERVICE_ROTATION
/*control_port->println(abs(elevation - target_elevation));
read_elevation(0);
control_port->println(abs(elevation - target_elevation));
control_port->println();*/
          #if defined(FEATURE_PARK)
          if ((park_status == PARK_INITIATED) && (az_state == IDLE)) {
            park_status = PARKED;
          }
          #endif // defined(FEATURE_PARK)

        }
      }
    } else {
      read_elevation(0);
      if ((abs(elevation - target_elevation) <= (ELEVATION_TOLERANCE * HEADING_MULTIPLIER)) || ((elevation < target_elevation) && ((target_elevation - elevation) < ((ELEVATION_TOLERANCE + 5) * HEADING_MULTIPLIER)))) {
        #ifndef OPTION_NO_ELEVATION_CHECK_TARGET_DELAY
        delay(50);
        #endif //OPTION_NO_ELEVATION_CHECK_TARGET_DELAY
        read_elevation(0);
        if ((abs(elevation - target_elevation) <= (ELEVATION_TOLERANCE * HEADING_MULTIPLIER)) || ((elevation < target_elevation) && ((target_elevation - elevation) < ((ELEVATION_TOLERANCE + 5) * HEADING_MULTIPLIER)))) {
          rotator(DEACTIVATE, UP);
          rotator(DEACTIVATE, DOWN);
          el_state = IDLE;
          el_request_queue_state = NONE;
          #ifdef DEBUG_SERVICE_ROTATION
          debug_print("service_rotation: IDLE");
          #endif // DEBUG_SERVICE_ROTATION
/*control_port->println(abs(elevation - target_elevation));
read_elevation(0);
control_port->println(abs(elevation - target_elevation));
control_port->println();*/
          #if defined(FEATURE_PARK)
          if ((park_status == PARK_INITIATED) && (az_state == IDLE)) {
            park_status = PARKED;
          }
          #endif // defined(FEATURE_PARK)
        }
      }
    }
  }




  #endif // FEATURE_ELEVATION_CONTROL

  #if defined(FEATURE_AZ_POSITION_INCREMENTAL_ENCODER) || defined(FEATURE_EL_POSITION_INCREMENTAL_ENCODER)
  service_rotation_lock = 0;
  #endif

} /* service_rotation */
// --------------------------------------------------------------
void stop_all_tracking(){


  #ifdef FEATURE_MOON_TRACKING
  moon_tracking_active = 0;
  #endif // FEATURE_MOON_TRACKING
  #ifdef FEATURE_SUN_TRACKING
  sun_tracking_active = 0;
  #endif // FEATURE_SUN_TRACKING

}

// --------------------------------------------------------------
void service_request_queue(){

// xxxx

  int work_target_raw_azimuth = 0;
  byte direction_to_go = 0;
  byte within_tolerance_flag = 0;

  if (az_request_queue_state == IN_QUEUE) {


    #ifdef FEATURE_POWER_SWITCH
    last_activity_time = millis();
    #endif //FEATURE_POWER_SWITCH

    #ifdef DEBUG_SERVICE_REQUEST_QUEUE
    debug_print("service_request_queue: AZ ");
    #endif // DEBUG_SERVICE_REQUEST_QUEUE

    switch (az_request) {
      case (REQUEST_STOP):
        #ifdef DEBUG_SERVICE_REQUEST_QUEUE
        debug_print("REQUEST_STOP");
        #endif // DEBUG_SERVICE_REQUEST_QUEUE
        stop_all_tracking();
        #ifdef FEATURE_PARK
        deactivate_park();
        #endif // FEATURE_PARK
        if (az_state != IDLE) {
          if (az_slowdown_active) {
            if ((az_state == TIMED_SLOW_DOWN_CW) || (az_state == TIMED_SLOW_DOWN_CCW) || (az_state == SLOW_DOWN_CW) || (az_state == SLOW_DOWN_CCW)) {  // if we're already in timed slow down and we get another stop, do a hard stop
              rotator(DEACTIVATE, CW);
              rotator(DEACTIVATE, CCW);
              az_state = IDLE;
              az_request_queue_state = NONE;
            }
            if ((az_state == SLOW_START_CW) || (az_state == NORMAL_CW)) {
              az_state = INITIALIZE_TIMED_SLOW_DOWN_CW;
              az_request_queue_state = IN_PROGRESS_TIMED;
              az_last_rotate_initiation = millis();
            }
            if ((az_state == SLOW_START_CCW) || (az_state == NORMAL_CCW)) {
              az_state = INITIALIZE_TIMED_SLOW_DOWN_CCW;
              az_request_queue_state = IN_PROGRESS_TIMED;
              az_last_rotate_initiation = millis();
            }

          } else {
            rotator(DEACTIVATE, CW);
            rotator(DEACTIVATE, CCW);
            az_state = IDLE;
            az_request_queue_state = NONE;
          }
        } else {
          az_request_queue_state = NONE; // nothing to do - we clear the queue
        }
        #ifdef DEBUG_SERVICE_REQUEST_QUEUE
        if (debug_mode) {
          control_port->println();
        }
        #endif // DEBUG_SERVICE_REQUEST_QUEUE
        break; // REQUEST_STOP

      case (REQUEST_AZIMUTH):
        #ifdef DEBUG_SERVICE_REQUEST_QUEUE
        debug_print("REQUEST_AZIMUTH");
        #endif // DEBUG_SERVICE_REQUEST_QUEUE
        if ((az_request_parm >= 0) && (az_request_parm <= (360 * HEADING_MULTIPLIER))) {
          target_azimuth = az_request_parm;
          target_raw_azimuth = az_request_parm;
          if (target_azimuth == (360 * HEADING_MULTIPLIER)) {
            target_azimuth = 0;
          }
          if ((target_azimuth > (azimuth - (AZIMUTH_TOLERANCE * HEADING_MULTIPLIER))) && (target_azimuth < (azimuth + (AZIMUTH_TOLERANCE * HEADING_MULTIPLIER)))) {
            #ifdef DEBUG_SERVICE_REQUEST_QUEUE
            debug_print(" request within tolerance");
            #endif // DEBUG_SERVICE_REQUEST_QUEUE
            within_tolerance_flag = 1;
            az_request_queue_state = NONE;
          } else {  // target azimuth is not within tolerance, we need to rotate
            #ifdef DEBUG_SERVICE_REQUEST_QUEUE
            debug_print(" ->A");
            #endif // DEBUG_SERVICE_REQUEST_QUEUE
            work_target_raw_azimuth = target_azimuth;
            #ifdef DEBUG_SERVICE_REQUEST_QUEUE
            debug_print(" work_target_raw_azimuth:");
            debug_print_int(work_target_raw_azimuth / HEADING_MULTIPLIER);
            debug_print(" azimuth_starting_point:");
            debug_print_int(azimuth_starting_point);
            debug_print(" ");
            #endif // DEBUG_SERVICE_REQUEST_QUEUE

            if (work_target_raw_azimuth < (azimuth_starting_point * HEADING_MULTIPLIER)) {
              work_target_raw_azimuth = work_target_raw_azimuth + (360 * HEADING_MULTIPLIER);
              target_raw_azimuth = work_target_raw_azimuth;
              #ifdef DEBUG_SERVICE_REQUEST_QUEUE
              debug_print("->B");
              #endif // DEBUG_SERVICE_REQUEST_QUEUE
            }
            if ((work_target_raw_azimuth + (360 * HEADING_MULTIPLIER)) < ((azimuth_starting_point + azimuth_rotation_capability) * HEADING_MULTIPLIER)) { // is there a second possible heading in overlap?
              if (abs(raw_azimuth - work_target_raw_azimuth) < abs((work_target_raw_azimuth + (360 * HEADING_MULTIPLIER)) - raw_azimuth)) { // is second possible heading closer?
                #ifdef DEBUG_SERVICE_REQUEST_QUEUE
                debug_print("->C");
                #endif // DEBUG_SERVICE_REQUEST_QUEUE
                if (work_target_raw_azimuth  > raw_azimuth) { // not closer, use position in non-overlap
                  direction_to_go = CW;
                  #ifdef DEBUG_SERVICE_REQUEST_QUEUE
                  debug_print("->CW!");
                  #endif // DEBUG_SERVICE_REQUEST_QUEUE
                } else {
                  direction_to_go = CCW;
                  #ifdef DEBUG_SERVICE_REQUEST_QUEUE
                  debug_print("->CCW!");
                  #endif // DEBUG_SERVICE_REQUEST_QUEUE
                }
              } else { // go to position in overlap
                #ifdef DEBUG_SERVICE_REQUEST_QUEUE
                debug_print("->D");
                #endif // DEBUG_SERVICE_REQUEST_QUEUE
                target_raw_azimuth = work_target_raw_azimuth + (360 * HEADING_MULTIPLIER);
                if ((work_target_raw_azimuth + (360 * HEADING_MULTIPLIER)) > raw_azimuth) {
                  direction_to_go = CW;
                  #ifdef DEBUG_SERVICE_REQUEST_QUEUE
                  debug_print("->CW!");
                  #endif // DEBUG_SERVICE_REQUEST_QUEUE
                } else {
                  direction_to_go = CCW;
                  #ifdef DEBUG_SERVICE_REQUEST_QUEUE
                  debug_print("->CCW!");
                  #endif // DEBUG_SERVICE_REQUEST_QUEUE
                }
              }
            } else {  // no possible second heading in overlap
              #ifdef DEBUG_SERVICE_REQUEST_QUEUE
              debug_print("->E");
              #endif // DEBUG_SERVICE_REQUEST_QUEUE
              if (work_target_raw_azimuth  > raw_azimuth) {
                direction_to_go = CW;
              } else {
                direction_to_go = CCW;
              }
            }
          }
        } else {
          #ifdef DEBUG_SERVICE_REQUEST_QUEUE
          debug_print("->F");
          #endif // DEBUG_SERVICE_REQUEST_QUEUE
          if ((az_request_parm > (360 * HEADING_MULTIPLIER)) && (az_request_parm <= ((azimuth_starting_point + azimuth_rotation_capability) * HEADING_MULTIPLIER))) {
            target_azimuth = az_request_parm - (360 * HEADING_MULTIPLIER);
            target_raw_azimuth = az_request_parm;
            if (az_request_parm > raw_azimuth) {
              direction_to_go = CW;
            } else {
              direction_to_go = CCW;
            }
          } else {
            #ifdef DEBUG_SERVICE_REQUEST_QUEUE
            debug_print(" error: bogus azimuth request:");
            debug_print_int(az_request_parm);
            debug_println("");
            #endif // DEBUG_SERVICE_REQUEST_QUEUE
            rotator(DEACTIVATE, CW);
            rotator(DEACTIVATE, CCW);
            az_state = IDLE;
            az_request_queue_state = NONE;         
            return;
          }
        }
        if (direction_to_go == CW) {
          if (((az_state == SLOW_START_CCW) || (az_state == NORMAL_CCW) || (az_state == SLOW_DOWN_CCW) || (az_state == TIMED_SLOW_DOWN_CCW)) && (az_slowstart_active)) {
            az_state = INITIALIZE_DIR_CHANGE_TO_CW;
            #ifdef DEBUG_SERVICE_REQUEST_QUEUE
            debug_print(" INITIALIZE_DIR_CHANGE_TO_CW");
            #endif // DEBUG_SERVICE_REQUEST_QUEUE
          } else {
            if ((az_state != INITIALIZE_SLOW_START_CW) && (az_state != SLOW_START_CW) && (az_state != NORMAL_CW)) { // if we're already rotating CW, don't do anything
              // rotator(ACTIVATE,CW);
              if (az_slowstart_active) {
                az_state = INITIALIZE_SLOW_START_CW;
              } else { az_state = INITIALIZE_NORMAL_CW; };
            }
          }
        }
        if (direction_to_go == CCW) {
          if (((az_state == SLOW_START_CW) || (az_state == NORMAL_CW) || (az_state == SLOW_DOWN_CW) || (az_state == TIMED_SLOW_DOWN_CW)) && (az_slowstart_active)) {
            az_state = INITIALIZE_DIR_CHANGE_TO_CCW;
            #ifdef DEBUG_SERVICE_REQUEST_QUEUE
            debug_print(" INITIALIZE_DIR_CHANGE_TO_CCW");
            #endif // DEBUG_SERVICE_REQUEST_QUEUE
          } else {
            if ((az_state != INITIALIZE_SLOW_START_CCW) && (az_state != SLOW_START_CCW) && (az_state != NORMAL_CCW)) { // if we're already rotating CCW, don't do anything
              // rotator(ACTIVATE,CCW);
              if (az_slowstart_active) {
                az_state = INITIALIZE_SLOW_START_CCW;
              } else { az_state = INITIALIZE_NORMAL_CCW; };
            }
          }
        }
        if (!within_tolerance_flag) {
          az_request_queue_state = IN_PROGRESS_TO_TARGET;
          az_last_rotate_initiation = millis();
        }
        #ifdef DEBUG_SERVICE_REQUEST_QUEUE
        if (debug_mode) {
          control_port->println();
        }
        #endif // DEBUG_SERVICE_REQUEST_QUEUE
        break; // REQUEST_AZIMUTH

      case (REQUEST_AZIMUTH_RAW):
        #ifdef DEBUG_SERVICE_REQUEST_QUEUE
        debug_print("REQUEST_AZIMUTH_RAW");
        #endif // DEBUG_SERVICE_REQUEST_QUEUE
        target_raw_azimuth = az_request_parm;
        target_azimuth = target_raw_azimuth;
        if (target_azimuth >= (360 * HEADING_MULTIPLIER)) {
          target_azimuth = target_azimuth - (360 * HEADING_MULTIPLIER);
        }

        if (((abs(raw_azimuth - target_raw_azimuth) < (AZIMUTH_TOLERANCE * HEADING_MULTIPLIER))) && (az_state == IDLE)) {
          #ifdef DEBUG_SERVICE_REQUEST_QUEUE
          debug_print(" request within tolerance");
          #endif // DEBUG_SERVICE_REQUEST_QUEUE
          az_request_queue_state = NONE;
          within_tolerance_flag = 1;
        } else {
          if (target_raw_azimuth > raw_azimuth) {
            if (((az_state == SLOW_START_CCW) || (az_state == NORMAL_CCW) || (az_state == SLOW_DOWN_CCW) || (az_state == TIMED_SLOW_DOWN_CCW)) && (az_slowstart_active)) {
              az_state = INITIALIZE_DIR_CHANGE_TO_CW;
              #ifdef DEBUG_SERVICE_REQUEST_QUEUE
              debug_print(" INITIALIZE_DIR_CHANGE_TO_CW");
              #endif // DEBUG_SERVICE_REQUEST_QUEUE
            } else {
              if ((az_state != INITIALIZE_SLOW_START_CW) && (az_state != SLOW_START_CW) && (az_state != NORMAL_CW)) { // if we're already rotating CW, don't do anything
                if (az_slowstart_active) {
                  az_state = INITIALIZE_SLOW_START_CW;
                } else { az_state = INITIALIZE_NORMAL_CW; };
              }
            }
          }
          if (target_raw_azimuth < raw_azimuth) {
            if (((az_state == SLOW_START_CW) || (az_state == NORMAL_CW) || (az_state == SLOW_DOWN_CW) || (az_state == TIMED_SLOW_DOWN_CW)) && (az_slowstart_active)) {
              az_state = INITIALIZE_DIR_CHANGE_TO_CCW;
              #ifdef DEBUG_SERVICE_REQUEST_QUEUE
              debug_print(" INITIALIZE_DIR_CHANGE_TO_CCW");
              #endif // DEBUG_SERVICE_REQUEST_QUEUE
            } else {
              if ((az_state != INITIALIZE_SLOW_START_CCW) && (az_state != SLOW_START_CCW) && (az_state != NORMAL_CCW)) { // if we're already rotating CCW, don't do anything
                if (az_slowstart_active) {
                  az_state = INITIALIZE_SLOW_START_CCW;
                } else { az_state = INITIALIZE_NORMAL_CCW; };
              }
            }
          }
          if (!within_tolerance_flag) {
            az_request_queue_state = IN_PROGRESS_TO_TARGET;
            az_last_rotate_initiation = millis();
          }
        }
      #ifdef DEBUG_SERVICE_REQUEST_QUEUE
        if (debug_mode) {
          control_port->println();
        }
      #endif // DEBUG_SERVICE_REQUEST_QUEUE
        break; // REQUEST_AZIMUTH_RAW

      case (REQUEST_CW):
        #ifdef DEBUG_SERVICE_REQUEST_QUEUE
        debug_print("REQUEST_CW");
        #endif // DEBUG_SERVICE_REQUEST_QUEUE
        stop_all_tracking();
        #ifdef FEATURE_PARK
        deactivate_park();
        #endif // FEATURE_PARK
        if (((az_state == SLOW_START_CCW) || (az_state == NORMAL_CCW) || (az_state == SLOW_DOWN_CCW) || (az_state == TIMED_SLOW_DOWN_CCW)) && (az_slowstart_active)) {
          az_state = INITIALIZE_DIR_CHANGE_TO_CW;
          #ifdef DEBUG_SERVICE_REQUEST_QUEUE
          debug_print(" INITIALIZE_DIR_CHANGE_TO_CW");
          #endif // DEBUG_SERVICE_REQUEST_QUEUE
        } else {
          if ((az_state != SLOW_START_CW) && (az_state != NORMAL_CW)) {
            // rotator(ACTIVATE,CW);
            if (az_slowstart_active) {
              az_state = INITIALIZE_SLOW_START_CW;
            } else { 
              az_state = INITIALIZE_NORMAL_CW;
            };
          }
        }
        az_request_queue_state = NONE;
        az_last_rotate_initiation = millis();
        #ifdef DEBUG_SERVICE_REQUEST_QUEUE
        if (debug_mode) {
          control_port->println();
        }
        #endif // DEBUG_SERVICE_REQUEST_QUEUE
        break; // REQUEST_CW

      case (REQUEST_CCW):
        #ifdef DEBUG_SERVICE_REQUEST_QUEUE
        debug_print("REQUEST_CCW");
        #endif // DEBUG_SERVICE_REQUEST_QUEUE
        stop_all_tracking();
        #ifdef FEATURE_PARK
        deactivate_park();
        #endif // FEATURE_PARK
        if (((az_state == SLOW_START_CW) || (az_state == NORMAL_CW) || (az_state == SLOW_DOWN_CW) || (az_state == TIMED_SLOW_DOWN_CW)) && (az_slowstart_active)) {
          az_state = INITIALIZE_DIR_CHANGE_TO_CCW;
          #ifdef DEBUG_SERVICE_REQUEST_QUEUE
          debug_print(" INITIALIZE_DIR_CHANGE_TO_CCW");
          #endif // DEBUG_SERVICE_REQUEST_QUEUE
        } else {
          if ((az_state != SLOW_START_CCW) && (az_state != NORMAL_CCW)) {
            // rotator(ACTIVATE,CCW);
            if (az_slowstart_active) {
              az_state = INITIALIZE_SLOW_START_CCW;
            } else { az_state = INITIALIZE_NORMAL_CCW; };
          }
        }
        az_request_queue_state = NONE;
        az_last_rotate_initiation = millis();
        #ifdef DEBUG_SERVICE_REQUEST_QUEUE
        if (debug_mode) {
          control_port->println();
        }
        #endif // DEBUG_SERVICE_REQUEST_QUEUE
        break; // REQUEST_CCW

      case (REQUEST_KILL):
        #ifdef DEBUG_SERVICE_REQUEST_QUEUE
        debug_print("REQUEST_KILL");
        #endif // DEBUG_SERVICE_REQUEST_QUEUE
        stop_all_tracking();
        #ifdef FEATURE_PARK
        deactivate_park();
        #endif // FEATURE_PARK
        rotator(DEACTIVATE, CW);
        rotator(DEACTIVATE, CCW);
        az_state = IDLE;
        az_request_queue_state = NONE;
        #ifdef DEBUG_SERVICE_REQUEST_QUEUE
        debug_println("");
        #endif // DEBUG_SERVICE_REQUEST_QUEUE
        break; // REQUEST_KILL
    } /* switch */

    #ifdef FEATURE_LCD_DISPLAY
    if (az_request_queue_state != IN_QUEUE) {push_lcd_update = 1;}
    #endif //FEATURE_LCD_DISPLAY
  }

  #ifdef FEATURE_ELEVATION_CONTROL
  if (el_request_queue_state == IN_QUEUE) {

    #ifdef FEATURE_POWER_SWITCH
    last_activity_time = millis();
    #endif //FEATURE_POWER_SWITCH

    within_tolerance_flag = 0;
    #ifdef DEBUG_SERVICE_REQUEST_QUEUE
    debug_print("service_request_queue: EL ");
    #endif // DEBUG_SERVICE_REQUEST_QUEUE
    switch (el_request) {
      case (REQUEST_ELEVATION):
        #ifdef DEBUG_SERVICE_REQUEST_QUEUE
        debug_print("REQUEST_ELEVATION ");
        #endif // DEBUG_SERVICE_REQUEST_QUEUE
        target_elevation = el_request_parm;

        if (target_elevation > (ELEVATION_MAXIMUM_DEGREES * HEADING_MULTIPLIER)) {
          target_elevation = ELEVATION_MAXIMUM_DEGREES * HEADING_MULTIPLIER;
          #ifdef DEBUG_SERVICE_REQUEST_QUEUE
          if (debug_mode) {
            control_port->print(F("REQUEST_ELEVATION: target_elevation > ELEVATION_MAXIMUM_DEGREES"));
          }
          #endif // DEBUG_SERVICE_REQUEST_QUEUE
        }

        #ifdef OPTION_EL_MANUAL_ROTATE_LIMITS
        if (target_elevation < (EL_MANUAL_ROTATE_DOWN_LIMIT * HEADING_MULTIPLIER)) {
          target_elevation = EL_MANUAL_ROTATE_DOWN_LIMIT * HEADING_MULTIPLIER;
          #ifdef DEBUG_SERVICE_REQUEST_QUEUE
          if (debug_mode) {
            control_port->print(F("REQUEST_ELEVATION: target_elevation < EL_MANUAL_ROTATE_DOWN_LIMIT"));
          }
          #endif // DEBUG_SERVICE_REQUEST_QUEUE
        }
        if (target_elevation > (EL_MANUAL_ROTATE_UP_LIMIT * HEADING_MULTIPLIER)) {
          target_elevation = EL_MANUAL_ROTATE_UP_LIMIT * HEADING_MULTIPLIER;
          #ifdef DEBUG_SERVICE_REQUEST_QUEUE
          if (debug_mode) {
            control_port->print(F("REQUEST_ELEVATION: target_elevation > EL_MANUAL_ROTATE_UP_LIMIT"));
          }
          #endif // DEBUG_SERVICE_REQUEST_QUEUE
        }
        #endif // OPTION_EL_MANUAL_ROTATE_LIMITS

        if (abs(target_elevation - elevation) < (ELEVATION_TOLERANCE * HEADING_MULTIPLIER)) {
          #ifdef DEBUG_SERVICE_REQUEST_QUEUE
          if (debug_mode) {
            control_port->println(F("requested elevation within tolerance"));
          }
          #endif // DEBUG_SERVICE_REQUEST_QUEUE
          within_tolerance_flag = 1;
          el_request_queue_state = NONE;
        } else {
          if (target_elevation > elevation) {
            if (((el_state == SLOW_START_DOWN) || (el_state == NORMAL_DOWN) || (el_state == SLOW_DOWN_DOWN) || (el_state == TIMED_SLOW_DOWN_DOWN)) && (el_slowstart_active)) {
              el_state = INITIALIZE_DIR_CHANGE_TO_UP;
                #ifdef DEBUG_SERVICE_REQUEST_QUEUE
              if (debug_mode) {
                control_port->println(F(" INITIALIZE_DIR_CHANGE_TO_UP"));
              }
                #endif // DEBUG_SERVICE_REQUEST_QUEUE
            } else {
              if ((el_state != INITIALIZE_SLOW_START_UP) && (el_state != SLOW_START_UP) && (el_state != NORMAL_UP)) { // if we're already rotating UP, don't do anything
                if (el_slowstart_active) {
                  el_state = INITIALIZE_SLOW_START_UP;
                } else { el_state = INITIALIZE_NORMAL_UP; };
              }
            }
          } // (target_elevation > elevation)
          if (target_elevation < elevation) {
            if (((el_state == SLOW_START_UP) || (el_state == NORMAL_UP) || (el_state == SLOW_DOWN_UP) || (el_state == TIMED_SLOW_DOWN_UP)) && (el_slowstart_active)) {
              el_state = INITIALIZE_DIR_CHANGE_TO_DOWN;
              #ifdef DEBUG_SERVICE_REQUEST_QUEUE
              if (debug_mode) {
                control_port->println(F(" INITIALIZE_DIR_CHANGE_TO_DOWN"));
              }
              #endif // DEBUG_SERVICE_REQUEST_QUEUE
            } else {
              if ((el_state != INITIALIZE_SLOW_START_DOWN) && (el_state != SLOW_START_DOWN) && (el_state != NORMAL_DOWN)) { // if we're already rotating DOWN, don't do anything
                if (el_slowstart_active) {
                  el_state = INITIALIZE_SLOW_START_DOWN;
                } else { el_state = INITIALIZE_NORMAL_DOWN; };
              }
            }
          }  // (target_elevation < elevation)
        }  // (abs(target_elevation - elevation) < ELEVATION_TOLERANCE)
        if (!within_tolerance_flag) {
          el_request_queue_state = IN_PROGRESS_TO_TARGET;
          el_last_rotate_initiation = millis();
        }
        #ifdef DEBUG_SERVICE_REQUEST_QUEUE
        if (debug_mode) {
          control_port->println();
        }
        #endif // DEBUG_SERVICE_REQUEST_QUEUE
        break; // REQUEST_ELEVATION

      case (REQUEST_UP):
        #ifdef DEBUG_SERVICE_REQUEST_QUEUE
        if (debug_mode) {
          control_port->println(F("REQUEST_UP"));
        }
        #endif // DEBUG_SERVICE_REQUEST_QUEUE
        stop_all_tracking();
        #ifdef FEATURE_PARK
        deactivate_park();
        #endif // FEATURE_PARK
        if (((el_state == SLOW_START_DOWN) || (el_state == NORMAL_DOWN) || (el_state == SLOW_DOWN_DOWN) || (el_state == TIMED_SLOW_DOWN_DOWN)) && (el_slowstart_active)) {
          el_state = INITIALIZE_DIR_CHANGE_TO_UP;
          #ifdef DEBUG_SERVICE_REQUEST_QUEUE
          if (debug_mode) {
            control_port->println(F("service_request_queue: INITIALIZE_DIR_CHANGE_TO_UP"));
          }
          #endif // DEBUG_SERVICE_REQUEST_QUEUE
        } else {
          if ((el_state != SLOW_START_UP) && (el_state != NORMAL_UP)) {
            if (el_slowstart_active) {
              el_state = INITIALIZE_SLOW_START_UP;
            } else { el_state = INITIALIZE_NORMAL_UP; };
          }
        }
        el_request_queue_state = NONE;
        el_last_rotate_initiation = millis();
          #ifdef DEBUG_SERVICE_REQUEST_QUEUE
        if (debug_mode) {
          control_port->println();
        }
          #endif // DEBUG_SERVICE_REQUEST_QUEUE
        break; // REQUEST_UP

      case (REQUEST_DOWN):
        #ifdef DEBUG_SERVICE_REQUEST_QUEUE
        if (debug_mode) {
          control_port->println(F("REQUEST_DOWN"));
        }
        #endif // DEBUG_SERVICE_REQUEST_QUEUE
        stop_all_tracking();
        #ifdef FEATURE_PARK
        deactivate_park();
        #endif // FEATURE_PARK
        if (((el_state == SLOW_START_UP) || (el_state == NORMAL_UP) || (el_state == SLOW_DOWN_UP) || (el_state == TIMED_SLOW_DOWN_UP)) && (el_slowstart_active)) {
          el_state = INITIALIZE_DIR_CHANGE_TO_DOWN;
          #ifdef DEBUG_SERVICE_REQUEST_QUEUE
          if (debug_mode) {
            control_port->println(F("service_request_queue: INITIALIZE_DIR_CHANGE_TO_DOWN"));
          }
          #endif // DEBUG_SERVICE_REQUEST_QUEUE
        } else {
          if ((el_state != SLOW_START_DOWN) && (el_state != NORMAL_DOWN)) {
            if (el_slowstart_active) {
              el_state = INITIALIZE_SLOW_START_DOWN;
            } else { el_state = INITIALIZE_NORMAL_DOWN; };
          }
        }
        el_request_queue_state = NONE;
        el_last_rotate_initiation = millis();
        #ifdef DEBUG_SERVICE_REQUEST_QUEUE
        if (debug_mode) {
          control_port->println();
        }
        #endif // DEBUG_SERVICE_REQUEST_QUEUE
        break; // REQUEST_DOWN

      case (REQUEST_STOP):
        #ifdef DEBUG_SERVICE_REQUEST_QUEUE
        if (debug_mode) {
          control_port->println(F("REQUEST_STOP"));
        }
        #endif // DEBUG_SERVICE_REQUEST_QUEUE
        stop_all_tracking();
        #ifdef FEATURE_PARK
        deactivate_park();
        #endif // FEATURE_PARK
        if (el_state != IDLE) {
          if (el_slowdown_active) {
            if ((el_state == TIMED_SLOW_DOWN_UP) || (el_state == TIMED_SLOW_DOWN_DOWN) || (el_state == SLOW_DOWN_UP) || (el_state == SLOW_DOWN_DOWN)) {  // if we're already in timed slow down and we get another stop, do a hard stop
              rotator(DEACTIVATE, UP);
              rotator(DEACTIVATE, DOWN);
              el_state = IDLE;
              el_request_queue_state = NONE;
            }
            if ((el_state == SLOW_START_UP) || (el_state == NORMAL_UP)) {
              el_state = INITIALIZE_TIMED_SLOW_DOWN_UP;
              el_request_queue_state = IN_PROGRESS_TIMED;
              el_last_rotate_initiation = millis();
            }
            if ((el_state == SLOW_START_DOWN) || (el_state == NORMAL_DOWN)) {
              el_state = INITIALIZE_TIMED_SLOW_DOWN_DOWN;
              el_request_queue_state = IN_PROGRESS_TIMED;
              el_last_rotate_initiation = millis();
            }
          } else {
            rotator(DEACTIVATE, UP);
            rotator(DEACTIVATE, DOWN);
            el_state = IDLE;
            el_request_queue_state = NONE;
          }
        } else {
          el_request_queue_state = NONE; // nothing to do, we're already in IDLE state
        }
        #ifdef DEBUG_SERVICE_REQUEST_QUEUE
        if (debug_mode) {
          control_port->println();
        }
        #endif // DEBUG_SERVICE_REQUEST_QUEUE
        break; // REQUEST_STOP

      case (REQUEST_KILL):
        #ifdef DEBUG_SERVICE_REQUEST_QUEUE
        if (debug_mode) {
          control_port->println(F("REQUEST_KILL"));
        }
        #endif // DEBUG_SERVICE_REQUEST_QUEUE
        stop_all_tracking();
        #ifdef FEATURE_PARK
        deactivate_park();
        #endif // FEATURE_PARK
        rotator(DEACTIVATE, UP);
        rotator(DEACTIVATE, DOWN);
        el_state = IDLE;
        el_request_queue_state = NONE;
        #ifdef DEBUG_SERVICE_REQUEST_QUEUE
        if (debug_mode) {
          control_port->println();
        }
        #endif // DEBUG_SERVICE_REQUEST_QUEUE
        break; // REQUEST_KILL
    } /* switch */

    #ifdef FEATURE_LCD_DISPLAY
    if (el_request_queue_state != IN_QUEUE) {push_lcd_update = 1;}
    #endif //FEATURE_LCD_DISPLAY

  } // (el_request_queue_state == IN_QUEUE)
  #endif // FEATURE_ELEVATION_CONTROL


} /* service_request_queue */

// --------------------------------------------------------------
void check_for_dirty_configuration(){

  static unsigned long last_config_write_time = 0;

  if ((configuration_dirty) && ((millis() - last_config_write_time) > (EEPROM_WRITE_DIRTY_CONFIG_TIME * 1000))) {
    write_settings_to_eeprom();
    last_config_write_time = millis();
  }

}

// --------------------------------------------------------------
byte current_az_state(){

  if ((az_state == SLOW_START_CW) || (az_state == NORMAL_CW) || (az_state == SLOW_DOWN_CW) || (az_state == TIMED_SLOW_DOWN_CW)) {
    return ROTATING_CW;
  }
  if ((az_state == SLOW_START_CCW) || (az_state == NORMAL_CCW) || (az_state == SLOW_DOWN_CCW) || (az_state == TIMED_SLOW_DOWN_CCW)) {
    return ROTATING_CCW;
  }
  return NOT_DOING_ANYTHING;

}
// --------------------------------------------------------------
#ifdef FEATURE_ELEVATION_CONTROL
byte current_el_state(){

  if ((el_state == SLOW_START_UP) || (el_state == NORMAL_UP) || (el_state == SLOW_DOWN_UP) || (el_state == TIMED_SLOW_DOWN_UP)) {
    return ROTATING_UP;
  }
  if ((el_state == SLOW_START_DOWN) || (el_state == NORMAL_DOWN) || (el_state == SLOW_DOWN_DOWN) || (el_state == TIMED_SLOW_DOWN_DOWN)) {
    return ROTATING_DOWN;
  }
  return NOT_DOING_ANYTHING;

}
#endif // FEATURE_ELEVATION_CONTROL
// --------------------------------------------------------------
#ifdef FEATURE_AZ_POSITION_PULSE_INPUT
void az_position_pulse_interrupt_handler(){


 #ifdef DEBUG_POSITION_PULSE_INPUT
  // az_position_pule_interrupt_handler_flag++;
  az_pulse_counter++;
  #endif // DEBUG_POSITION_PULSE_INPUT

  if (current_az_state() == ROTATING_CW) {
    az_position_pulse_input_azimuth += AZ_POSITION_PULSE_DEG_PER_PULSE;
    last_known_az_state = ROTATING_CW;
  } else {
    if (current_az_state() == ROTATING_CCW) {
      az_position_pulse_input_azimuth -= AZ_POSITION_PULSE_DEG_PER_PULSE;
      last_known_az_state = ROTATING_CCW;
    } else {
          #ifndef OPTION_PULSE_IGNORE_AMBIGUOUS_PULSES
      if (last_known_az_state == ROTATING_CW) {
        az_position_pulse_input_azimuth += AZ_POSITION_PULSE_DEG_PER_PULSE;
      } else {
        if (last_known_az_state == ROTATING_CCW) {
          az_position_pulse_input_azimuth -= AZ_POSITION_PULSE_DEG_PER_PULSE;
        }
      }
            #endif // OPTION_PULSE_IGNORE_AMBIGUOUS_PULSES
            #ifdef DEBUG_POSITION_PULSE_INPUT
      az_pulse_counter_ambiguous++;
            #endif // DEBUG_POSITION_PULSE_INPUT
    }
  }

  #ifdef OPTION_AZ_POSITION_PULSE_HARD_LIMIT
  if (az_position_pulse_input_azimuth < azimuth_starting_point) {
    az_position_pulse_input_azimuth = azimuth_starting_point;
  }
  if (az_position_pulse_input_azimuth > (azimuth_starting_point + azimuth_rotation_capability)) {
    az_position_pulse_input_azimuth = (azimuth_starting_point + azimuth_rotation_capability);
  }
  #else
  if (az_position_pulse_input_azimuth < 0) {
    az_position_pulse_input_azimuth += 360;
  }
  if (az_position_pulse_input_azimuth >= 360) {
    az_position_pulse_input_azimuth -= 360;
  }
  #endif // OPTION_AZ_POSITION_PULSE_HARD_LIMIT

} /* az_position_pulse_interrupt_handler */
#endif // FEATURE_AZ_POSITION_PULSE_INPUT
// --------------------------------------------------------------
#ifdef FEATURE_ELEVATION_CONTROL
#ifdef FEATURE_EL_POSITION_PULSE_INPUT
void el_position_pulse_interrupt_handler(){

  #ifdef DEBUG_POSITION_PULSE_INPUT
  // el_position_pule_interrupt_handler_flag++;
  el_pulse_counter++;
  #endif // DEBUG_POSITION_PULSE_INPUT



  #ifdef OPTION_EL_PULSE_DEBOUNCE //---------------------------------------------
  if ((millis()-last_el_pulse_debounce) > EL_POSITION_PULSE_DEBOUNCE) {
    if (current_el_state() == ROTATING_UP) {
      el_position_pulse_input_elevation += EL_POSITION_PULSE_DEG_PER_PULSE;
      last_known_el_state = ROTATING_UP;
    } else {
      if (current_el_state() == ROTATING_DOWN) {
        el_position_pulse_input_elevation -= EL_POSITION_PULSE_DEG_PER_PULSE;
        last_known_el_state = ROTATING_DOWN;
      } else {
        #ifndef OPTION_PULSE_IGNORE_AMBIGUOUS_PULSES
        if (last_known_el_state == ROTATING_UP) {
          el_position_pulse_input_elevation += EL_POSITION_PULSE_DEG_PER_PULSE;
        } else {
          if (last_known_el_state == ROTATING_DOWN) {
            el_position_pulse_input_elevation -= EL_POSITION_PULSE_DEG_PER_PULSE;
          }
        }
        #endif // OPTION_PULSE_IGNORE_AMBIGUOUS_PULSES
        #ifdef DEBUG_POSITION_PULSE_INPUT
        el_pulse_counter_ambiguous++;
        #endif // DEBUG_POSITION_PULSE_INPUT
      }
    }
    last_el_pulse_debounce = millis();
  }

  #else //OPTION_EL_PULSE_DEBOUNCE -----------------------



  if (current_el_state() == ROTATING_UP) {
    el_position_pulse_input_elevation += EL_POSITION_PULSE_DEG_PER_PULSE;
    last_known_el_state = ROTATING_UP;
  } else {
    if (current_el_state() == ROTATING_DOWN) {
      el_position_pulse_input_elevation -= EL_POSITION_PULSE_DEG_PER_PULSE;
      last_known_el_state = ROTATING_DOWN;
    } else {
      #ifndef OPTION_PULSE_IGNORE_AMBIGUOUS_PULSES
      if (last_known_el_state == ROTATING_UP) {
        el_position_pulse_input_elevation += EL_POSITION_PULSE_DEG_PER_PULSE;
      } else {
        if (last_known_el_state == ROTATING_DOWN) {
          el_position_pulse_input_elevation -= EL_POSITION_PULSE_DEG_PER_PULSE;
        }
      }
      #endif // OPTION_PULSE_IGNORE_AMBIGUOUS_PULSES
      #ifdef DEBUG_POSITION_PULSE_INPUT
      el_pulse_counter_ambiguous++;
      #endif // DEBUG_POSITION_PULSE_INPUT
    }
  }
  #endif //OPTION_EL_PULSE_DEBOUNCE --------------------------

  #ifdef OPTION_EL_POSITION_PULSE_HARD_LIMIT
  if (el_position_pulse_input_elevation < 0) {
    el_position_pulse_input_elevation = 0;
  }
  if (el_position_pulse_input_elevation > ELEVATION_MAXIMUM_DEGREES) {
    el_position_pulse_input_elevation = ELEVATION_MAXIMUM_DEGREES;
  }
  #endif // OPTION_EL_POSITION_PULSE_HARD_LIMIT


} /* el_position_pulse_interrupt_handler */
#endif // FEATURE_EL_POSITION_PULSE_INPUT
#endif // FEATURE_ELEVATION_CONTROL
// --------------------------------------------------------------------------
#if defined(FEATURE_MASTER_WITH_SERIAL_SLAVE) || defined(FEATURE_MASTER_WITH_ETHERNET_SLAVE)
byte submit_remote_command(byte remote_command_to_send, byte parm1, int parm2){

  #ifdef FEATURE_MASTER_WITH_ETHERNET_SLAVE
  char ethernet_send_string[32];
  char temp_string[32];
  #endif //FEATURE_MASTER_WITH_ETHERNET_SLAVE

  #ifdef FEATURE_MASTER_WITH_ETHERNET_SLAVE
  if (ethernetslavelinkclient0_state != ETHERNET_SLAVE_CONNECTED){return 0;}
  #endif //FEATURE_MASTER_WITH_ETHERNET_SLAVE

  if ((remote_unit_command_submitted && ((remote_command_to_send == REMOTE_UNIT_AZ_COMMAND) || (remote_command_to_send == REMOTE_UNIT_EL_COMMAND) || (remote_command_to_send == REMOTE_UNIT_CL_COMMAND))) || suspend_remote_commands) {
    return 0;
  } else {
    switch (remote_command_to_send) {
      case REMOTE_UNIT_CL_COMMAND:
        #ifdef FEATURE_MASTER_WITH_SERIAL_SLAVE
        remote_unit_port->println("CL");
        #endif //FEATURE_MASTER_WITH_SERIAL_SLAVE
        #ifdef FEATURE_MASTER_WITH_ETHERNET_SLAVE
        ethernet_slave_link_send("CL");
        #endif //FEATURE_MASTER_WITH_ETHERNET_SLAVE
        #if defined(FEATURE_REMOTE_UNIT_SLAVE) || defined(FEATURE_YAESU_EMULATION) || defined(FEATURE_EASYCOM_EMULATION)
        if (remote_port_tx_sniff) {control_port->println("CL");}
        #endif
        remote_unit_command_submitted = REMOTE_UNIT_CL_COMMAND;
        break;


      case REMOTE_UNIT_AZ_COMMAND:
        #ifdef FEATURE_MASTER_WITH_SERIAL_SLAVE
        remote_unit_port->println("AZ");
        #endif //FEATURE_MASTER_WITH_SERIAL_SLAVE
        #ifdef FEATURE_MASTER_WITH_ETHERNET_SLAVE
        ethernet_slave_link_send("AZ");
        #endif //FEATURE_MASTER_WITH_ETHERNET_SLAVE
        #if defined(FEATURE_REMOTE_UNIT_SLAVE) || defined(FEATURE_YAESU_EMULATION) || defined(FEATURE_EASYCOM_EMULATION)
        if (remote_port_tx_sniff) {control_port->println("AZ");}
        #endif
        remote_unit_command_submitted = REMOTE_UNIT_AZ_COMMAND;
        break;

      case REMOTE_UNIT_EL_COMMAND:
        #ifdef FEATURE_MASTER_WITH_SERIAL_SLAVE
        remote_unit_port->println("EL");
        #endif //FEATURE_MASTER_WITH_SERIAL_SLAVE
        #ifdef FEATURE_MASTER_WITH_ETHERNET_SLAVE
        ethernet_slave_link_send("EL");
        #endif //FEATURE_MASTER_WITH_ETHERNET_SLAVE        
        #if defined(FEATURE_REMOTE_UNIT_SLAVE) || defined(FEATURE_YAESU_EMULATION) || defined(FEATURE_EASYCOM_EMULATION)
        if (remote_port_tx_sniff) {control_port->println("EL");}
        #endif
        remote_unit_command_submitted = REMOTE_UNIT_EL_COMMAND;
        break;


      case REMOTE_UNIT_AW_COMMAND:
        #ifdef FEATURE_MASTER_WITH_SERIAL_SLAVE
        take_care_of_pending_remote_command();
        remote_unit_port->print("AW");
        parm1 = parm1 - 100;   // pin number
        if (parm1 < 10) {remote_unit_port->print("0");}
        remote_unit_port->print(parm1);
        if (parm2 < 10) {remote_unit_port->print("0");}
        if (parm2 < 100) {remote_unit_port->print("0");}
        remote_unit_port->println(parm2);
        #endif //FEATURE_MASTER_WITH_SERIAL_SLAVE

        #ifdef FEATURE_MASTER_WITH_ETHERNET_SLAVE
        take_care_of_pending_remote_command();
        strcpy(ethernet_send_string,"AW");
        parm1 = parm1 - 100;   // pin number
        if (parm1 < 10) {strcat(ethernet_send_string,"0");}
        dtostrf(parm1,0,0,temp_string);
        if (parm2 < 10) {strcat(ethernet_send_string,"0");}
        if (parm2 < 100) {strcat(ethernet_send_string,"0");}
        dtostrf(parm2,0,0,temp_string);
        strcat(ethernet_send_string,temp_string);
        ethernet_slave_link_send(ethernet_send_string);
        #endif //FEATURE_MASTER_WITH_ETHERNET_SLAVE

        remote_unit_command_submitted = REMOTE_UNIT_OTHER_COMMAND;
        break;

      case REMOTE_UNIT_DHL_COMMAND:
        #ifdef FEATURE_MASTER_WITH_SERIAL_SLAVE
        take_care_of_pending_remote_command();
        remote_unit_port->print("D");
        if (parm2 == HIGH) {remote_unit_port->print("H");} else {remote_unit_port->print("L");}
        parm1 = parm1 - 100;
        if (parm1 < 10) {remote_unit_port->print("0");}
        remote_unit_port->println(parm1);
        #endif //FEATURE_MASTER_WITH_SERIAL_SLAVE


        #ifdef FEATURE_MASTER_WITH_ETHERNET_SLAVE
        take_care_of_pending_remote_command();
        strcpy(ethernet_send_string,"D");
        if (parm2 == HIGH) {strcat(ethernet_send_string,"H");} else {strcat(ethernet_send_string,"L");}
        parm1 = parm1 - 100;
        if (parm1 < 10) {strcat(ethernet_send_string,"0");}
        dtostrf(parm1,0,0,temp_string);
        strcat(ethernet_send_string,temp_string);
        ethernet_slave_link_send(ethernet_send_string);
        #endif //FEATURE_MASTER_WITH_ETHERNET_SLAVE

        remote_unit_command_submitted = REMOTE_UNIT_OTHER_COMMAND;

        break;

      case REMOTE_UNIT_DOI_COMMAND:
        #ifdef FEATURE_MASTER_WITH_SERIAL_SLAVE
        take_care_of_pending_remote_command();
        remote_unit_port->print("D");
        if (parm2 == OUTPUT) {remote_unit_port->print("O");} else {remote_unit_port->print("I");}
        parm1 = parm1 - 100;
        if (parm1 < 10) {remote_unit_port->print("0");}
        remote_unit_port->println(parm1);
        remote_unit_command_submitted = REMOTE_UNIT_OTHER_COMMAND;
        // get_remote_port_ok_response();
        #endif //FEATURE_MASTER_WITH_SERIAL_SLAVE

        #ifdef FEATURE_MASTER_WITH_ETHERNET_SLAVE
        take_care_of_pending_remote_command();
        strcpy(ethernet_send_string,"D");
        if (parm2 == OUTPUT) {strcat(ethernet_send_string,"O");} else {strcat(ethernet_send_string,"I");}
        parm1 = parm1 - 100;
        if (parm1 < 10) {strcat(ethernet_send_string,"0");}
        dtostrf(parm1,0,0,temp_string);
        strcat(ethernet_send_string,temp_string);
        ethernet_slave_link_send(ethernet_send_string);
        remote_unit_command_submitted = REMOTE_UNIT_OTHER_COMMAND;
        #endif //FEATURE_MASTER_WITH_ETHERNET_SLAVE

        break;

      case REMOTE_UNIT_GS_COMMAND:
        #ifdef FEATURE_MASTER_WITH_SERIAL_SLAVE
        remote_unit_port->println("GS");
        #endif //FEATURE_MASTER_WITH_SERIAL_SLAVE
        #ifdef FEATURE_MASTER_WITH_ETHERNET_SLAVE
        ethernet_slave_link_send("GS");
        #endif //FEATURE_MASTER_WITH_ETHERNET_SLAVE
        #if defined(FEATURE_REMOTE_UNIT_SLAVE) || defined(FEATURE_YAESU_EMULATION) || defined(FEATURE_EASYCOM_EMULATION)
        if (remote_port_tx_sniff) {control_port->println("GS");}
        #endif
        remote_unit_command_submitted = REMOTE_UNIT_GS_COMMAND;
        break;

      case REMOTE_UNIT_RC_COMMAND:    
        #ifdef FEATURE_MASTER_WITH_SERIAL_SLAVE
        remote_unit_port->println("RC");
        #endif //FEATURE_MASTER_WITH_SERIAL_SLAVE
        #ifdef FEATURE_MASTER_WITH_ETHERNET_SLAVE
        ethernet_slave_link_send("RC");
        #endif //FEATURE_MASTER_WITH_ETHERNET_SLAVE
        #if defined(FEATURE_REMOTE_UNIT_SLAVE) || defined(FEATURE_YAESU_EMULATION) || defined(FEATURE_EASYCOM_EMULATION)
        if (remote_port_tx_sniff) {control_port->println("RC");}
        #endif
        remote_unit_command_submitted = REMOTE_UNIT_RC_COMMAND;
        break;

    } /* switch */
    last_remote_unit_command_time = millis();
    remote_unit_command_results_available = 0;
    return 1;
  }



} /* submit_remote_command */
#endif // defined(FEATURE_MASTER_WITH_SERIAL_SLAVE) || defined(FEATURE_MASTER_WITH_ETHERNET_SLAVE)

// --------------------------------------------------------------------------

#if defined(FEATURE_MASTER_WITH_SERIAL_SLAVE) || defined(FEATURE_MASTER_WITH_ETHERNET_SLAVE)
byte is_ascii_number(byte char_in){

  if ((char_in > 47) && (char_in < 58)) {
    return 1;
  } else {
    return 0;
  }

}\

 #endif // defined(FEATURE_MASTER_WITH_SERIAL_SLAVE) || defined(FEATURE_MASTER_WITH_ETHERNET_SLAVE)
// --------------------------------------------------------------------------
#if defined(FEATURE_MASTER_WITH_SERIAL_SLAVE) || defined(FEATURE_MASTER_WITH_ETHERNET_SLAVE)
void service_remote_communications_incoming_buffer(){


  #if defined(FEATURE_CLOCK) && defined(OPTION_SYNC_MASTER_CLOCK_TO_SLAVE)
  int temp_year = 0;
  byte temp_month = 0;
  byte temp_day = 0;
  byte temp_minute = 0;
  byte temp_hour = 0;
  byte temp_sec = 0;
  #endif // defined(FEATURE_CLOCK) && defined(OPTION_SYNC_MASTER_CLOCK_TO_SLAVE)

  float temp_float_latitude = 0;
  float temp_float_longitude = 0;



  byte good_data = 0;

  if (remote_unit_port_buffer_carriage_return_flag) {

    #ifdef DEBUG_SVC_REMOTE_COMM_INCOMING_BUFFER
    debug_print("service_remote_communications_incoming_buffer: remote_unit_port_buffer_index: ");
    debug_print_int(remote_unit_port_buffer_index);
    debug_print(" buffer: ");
    for (int x = 0; x < remote_unit_port_buffer_index; x++) {
      debug_write((char*)remote_unit_port_buffer[x]);
      debug_println("$");
    }
    #endif // DEBUG_SVC_REMOTE_COMM_INCOMING_BUFFER

    if (remote_unit_command_submitted) {   // this was a solicited response
      switch (remote_unit_command_submitted) {
        case REMOTE_UNIT_RC_COMMAND:  //RC+40.9946 -075.6596
          if ((remote_unit_port_buffer[0] == 'R') && (remote_unit_port_buffer[1] == 'C') && (remote_unit_port_buffer[5] == '.') && (remote_unit_port_buffer[10] == ' ') && (remote_unit_port_buffer[15] == '.')){
            temp_float_latitude = ((remote_unit_port_buffer[3]-48)*10) + (remote_unit_port_buffer[4]-48) + ((remote_unit_port_buffer[6]-48)/10.0) + ((remote_unit_port_buffer[7]-48)/100.0) + ((remote_unit_port_buffer[8]-48)/1000.0) + ((remote_unit_port_buffer[9]-48)/10000.0);
            if (remote_unit_port_buffer[2] == '-') {
              temp_float_latitude = temp_float_latitude * -1;
            }
            temp_float_longitude = ((remote_unit_port_buffer[12]-48)*100) + ((remote_unit_port_buffer[13]-48)*10) + (remote_unit_port_buffer[14]-48) + ((remote_unit_port_buffer[16]-48)/10.0)+ ((remote_unit_port_buffer[17]-48)/100.0) + ((remote_unit_port_buffer[18]-48)/1000.0) + ((remote_unit_port_buffer[19]-48)/10000.0);
            if (remote_unit_port_buffer[11] == '-') {
              temp_float_longitude = temp_float_longitude * -1;
            }
            if ((temp_float_latitude <= 90) && (temp_float_latitude >= -90) && (temp_float_longitude <= 180) && (temp_float_longitude >= -180)){
              latitude = temp_float_latitude;
              longitude = temp_float_longitude;
              #ifdef DEBUG_SYNC_MASTER_COORDINATES_TO_SLAVE
              debug_println("service_remote_communications_incoming_buffer: coordinates synced to slave");
              #endif //DEBUG_SYNC_MASTER_COORDINATES_TO_SLAVE              
            }         
            good_data = 1;
          }
          break;

        case REMOTE_UNIT_GS_COMMAND:
          if ((remote_unit_port_buffer[0] == 'G') && (remote_unit_port_buffer[1] == 'S')){
            if (remote_unit_port_buffer[2] == '1'){
              if (clock_status == SLAVE_SYNC) {clock_status = SLAVE_SYNC_GPS;}
              good_data = 1;
            } else {
              if (remote_unit_port_buffer[2] == '0') {good_data = 1;}
            }
          }
          break;


        case REMOTE_UNIT_CL_COMMAND:
          if ((remote_unit_port_buffer[0] == 'C') && (remote_unit_port_buffer[1] == 'L') &&
            (remote_unit_port_buffer[12] == ' ') && (remote_unit_port_buffer[21] == 'Z'))
          {
            #if defined(FEATURE_CLOCK) && defined(OPTION_SYNC_MASTER_CLOCK_TO_SLAVE)
            temp_year = ((remote_unit_port_buffer[2] - 48) * 1000) + ((remote_unit_port_buffer[3] - 48) * 100) + ((remote_unit_port_buffer[4] - 48) * 10) + (remote_unit_port_buffer[5] - 48);
            temp_month = ((remote_unit_port_buffer[7] - 48) * 10) + (remote_unit_port_buffer[8] - 48);
            temp_day = ((remote_unit_port_buffer[10] - 48) * 10) + (remote_unit_port_buffer[11] - 48);
            temp_hour = ((remote_unit_port_buffer[13] - 48) * 10) + (remote_unit_port_buffer[14] - 48);
            temp_minute = ((remote_unit_port_buffer[16] - 48) * 10) + (remote_unit_port_buffer[17] - 48);
            temp_sec = ((remote_unit_port_buffer[19] - 48) * 10) + (remote_unit_port_buffer[20] - 48);
            if ((temp_year > 2013) && (temp_year < 2070) &&
                (temp_month > 0) && (temp_month < 13) &&
                (temp_day > 0) && (temp_day < 32) &&
                (temp_hour >= 0) && (temp_hour < 24) &&
                (temp_minute >= 0) && (temp_minute < 60) &&
                (temp_sec >= 0) && (temp_sec < 60) ) {

              clock_year_set = temp_year;
              clock_month_set = temp_month;
              clock_day_set = temp_day;
              clock_hour_set = temp_hour;
              clock_min_set = temp_minute;
              clock_sec_set = temp_sec;
              millis_at_last_calibration = millis();
              #ifdef DEBUG_SYNC_MASTER_CLOCK_TO_SLAVE
              debug_println("service_remote_communications_incoming_buffer: clock synced to slave clock");
              #endif //DEBUG_SYNC_MASTER_CLOCK_TO_SLAVE
              good_data = 1;
              clock_synced_to_remote = 1;
              if (clock_status == FREE_RUNNING) {clock_status = SLAVE_SYNC;}
            } else {

              #ifdef DEBUG_SYNC_MASTER_CLOCK_TO_SLAVE
              debug_println("service_remote_communications_incoming_buffer: slave clock sync error");
              #endif //DEBUG_SYNC_MASTER_CLOCK_TO_SLAVE  
              if ((clock_status == SLAVE_SYNC) || (clock_status == SLAVE_SYNC_GPS)) {clock_status = FREE_RUNNING;}   
            }
            #endif // defined(FEATURE_CLOCK) && defined(OPTION_SYNC_MASTER_CLOCK_TO_SLAVE)

            #if !defined(FEATURE_CLOCK) || !defined(OPTION_SYNC_MASTER_CLOCK_TO_SLAVE)
            good_data = 1;
            #endif //!defined(FEATURE_CLOCK) || !defined(OPTION_SYNC_MASTER_CLOCK_TO_SLAVE)
          } else {
            #if defined(FEATURE_CLOCK) && defined(OPTION_SYNC_MASTER_CLOCK_TO_SLAVE)
            #ifdef DEBUG_SYNC_MASTER_CLOCK_TO_SLAVE
            debug_print("service_remote_communications_incoming_buffer: REMOTE_UNIT_CL_COMMAND format error.  remote_unit_port_buffer_index: ");
            debug_print_int(remote_unit_port_buffer_index);
            debug_println("");
            #endif //DEBUG_SYNC_MASTER_CLOCK_TO_SLAVE 
            if ((clock_status == SLAVE_SYNC) || (clock_status == SLAVE_SYNC_GPS)) {clock_status = FREE_RUNNING;} 
            #endif // defined(FEATURE_CLOCK) && defined(OPTION_SYNC_MASTER_CLOCK_TO_SLAVE)          
          }
          break;
        case REMOTE_UNIT_AZ_COMMAND:
          if ((remote_unit_port_buffer_index == 13) && (remote_unit_port_buffer[0] == 'A') && (remote_unit_port_buffer[1] == 'Z') &&
              (is_ascii_number(remote_unit_port_buffer[2])) && (is_ascii_number(remote_unit_port_buffer[3])) && (is_ascii_number(remote_unit_port_buffer[4])) && (is_ascii_number(remote_unit_port_buffer[6]))  && (is_ascii_number(remote_unit_port_buffer[7])) && (is_ascii_number(remote_unit_port_buffer[8])) && (is_ascii_number(remote_unit_port_buffer[9])) && (is_ascii_number(remote_unit_port_buffer[10])) && (is_ascii_number(remote_unit_port_buffer[11]))) {
            remote_unit_command_result_float = ((remote_unit_port_buffer[2] - 48) * 100) + ((remote_unit_port_buffer[3] - 48) * 10) + (remote_unit_port_buffer[4] - 48) + ((remote_unit_port_buffer[6] - 48) / 10.0) + ((remote_unit_port_buffer[7] - 48) / 100.0) + ((remote_unit_port_buffer[8] - 48) / 1000.0) + ((remote_unit_port_buffer[9] - 48) / 10000.0) + ((remote_unit_port_buffer[10] - 48) / 100000.0) + ((remote_unit_port_buffer[11] - 48) / 1000000.0);
            good_data = 1;
          }
          break;
        case REMOTE_UNIT_EL_COMMAND:
          if ((remote_unit_port_buffer_index == 14) && (remote_unit_port_buffer[0] == 'E') && (remote_unit_port_buffer[1] == 'L') &&
              (is_ascii_number(remote_unit_port_buffer[3])) && (is_ascii_number(remote_unit_port_buffer[4])) && (is_ascii_number(remote_unit_port_buffer[5])) && (is_ascii_number(remote_unit_port_buffer[7])) && (is_ascii_number(remote_unit_port_buffer[8])) && (is_ascii_number(remote_unit_port_buffer[9])) && (is_ascii_number(remote_unit_port_buffer[10])) && (is_ascii_number(remote_unit_port_buffer[11])) && (is_ascii_number(remote_unit_port_buffer[12]))) {
            remote_unit_command_result_float = ((remote_unit_port_buffer[3] - 48) * 100) + ((remote_unit_port_buffer[4] - 48) * 10) + (remote_unit_port_buffer[5] - 48) + ((remote_unit_port_buffer[7] - 48) / 10.0)  + ((remote_unit_port_buffer[8] - 48) / 100.0)  + ((remote_unit_port_buffer[9] - 48) / 1000.0)  + ((remote_unit_port_buffer[10] - 48) / 10000.0)  + ((remote_unit_port_buffer[11] - 48) / 100000.0)  + ((remote_unit_port_buffer[12] - 48) / 1000000.0);
            if (remote_unit_port_buffer[2] == '+') {
              good_data = 1;
            }
            if (remote_unit_port_buffer[2] == '-') {
              remote_unit_command_result_float = remote_unit_command_result_float * -1.0;
              good_data = 1;
            }
          }
          break;
        case REMOTE_UNIT_OTHER_COMMAND:
          if ((remote_unit_port_buffer[0] == 'O') && (remote_unit_port_buffer[1] == 'K')) {
            good_data = 1;
          }
          break;
      } /* switch */
      if (good_data) {
        if (remote_unit_command_submitted != REMOTE_UNIT_OTHER_COMMAND) {
          remote_unit_command_results_available = remote_unit_command_submitted;
        }
        remote_unit_good_results++;

        #ifdef DEBUG_SVC_REMOTE_COMM_INCOMING_BUFFER
        debug_print("service_remote_communications_incoming_buffer: remote_unit_command_results_available: ");
        debug_print_int(remote_unit_command_results_available);
        debug_print(" remote_unit_command_result_float: ");
        debug_print_float(remote_unit_command_result_float,2);
        debug_println("");
        #endif // DEBUG_SVC_REMOTE_COMM_INCOMING_BUFFER


      } else {

        #ifdef DEBUG_SVC_REMOTE_COMM_INCOMING_BUFFER_BAD_DATA
        debug_print("service_remote_communications_incoming_buffer: bad data: remote_unit_command_submitted: ");
        switch (remote_unit_command_submitted) {
          case REMOTE_UNIT_AZ_COMMAND: debug_print("REMOTE_UNIT_AZ_COMMAND"); break;
          case REMOTE_UNIT_EL_COMMAND: debug_print("REMOTE_UNIT_EL_COMMAND"); break;
          case REMOTE_UNIT_OTHER_COMMAND: debug_print("REMOTE_UNIT_OTHER_COMMAND"); break;
          default: debug_print("UNDEFINED"); break;
        }
        debug_print(" buffer_index:");
        debug_print_int(remote_unit_port_buffer_index);
        debug_print(" buffer: ");
        for (int x = 0; x < remote_unit_port_buffer_index; x++) {
          debug_write((char*)remote_unit_port_buffer[x]);
        }
        debug_println("$");
        #endif // DEBUG_SVC_REMOTE_COMM_INCOMING_BUFFER_BAD_DATA


        remote_unit_command_results_available = 0;
        remote_unit_bad_results++;
      }
      remote_unit_command_submitted = 0;
    } else {  // this was an unsolicited message

    }
    remote_unit_port_buffer_carriage_return_flag = 0;
    remote_unit_port_buffer_index = 0;
  }

  // has a command timed out?
  if ((remote_unit_command_submitted) && ((millis() - last_remote_unit_command_time) > REMOTE_UNIT_COMMAND_TIMEOUT_MS)) {

    #if defined(FEATURE_CLOCK) && defined(OPTION_SYNC_MASTER_CLOCK_TO_SLAVE)
    if ((remote_unit_command_submitted == REMOTE_UNIT_CL_COMMAND) && ((clock_status == SLAVE_SYNC) || (clock_status == SLAVE_SYNC_GPS))){
      clock_status = FREE_RUNNING;
    }
    #endif //defined(FEATURE_CLOCK) && defined(OPTION_SYNC_MASTER_CLOCK_TO_SLAVE)

    remote_unit_command_timeouts++;
    remote_unit_command_submitted = 0;
    remote_unit_port_buffer_index = 0;
    
  }

  // have characters been in the buffer for some time but no carriage return?
  if ((remote_unit_port_buffer_index) && (!remote_unit_command_submitted) && ((millis() - serial1_last_receive_time) > REMOTE_UNIT_COMMAND_TIMEOUT_MS)) {
    remote_unit_port_buffer_index = 0;
    remote_unit_incoming_buffer_timeouts++;
  }

} /* service_remote_communications_incoming_buffer */

#endif // defined(FEATURE_MASTER_WITH_SERIAL_SLAVE) || defined(FEATURE_MASTER_WITH_ETHERNET_SLAVE)
// --------------------------------------------------------------------------
#ifdef FEATURE_AZIMUTH_CORRECTION
float correct_azimuth(float azimuth_in){

  if (sizeof(azimuth_calibration_from) != sizeof(azimuth_calibration_to)) {
    return azimuth_in;
  }
  for (unsigned int x = 0; x < (sizeof(azimuth_calibration_from) - 2); x++) {
    if ((azimuth_in >= azimuth_calibration_from[x]) && (azimuth_in <= azimuth_calibration_from[x + 1])) {
      //return (map(azimuth_in * 10, azimuth_calibration_from[x] * 10, azimuth_calibration_from[x + 1] * 10, azimuth_calibration_to[x] * 10, azimuth_calibration_to[x + 1] * 10)) / 10.0;
      return (azimuth_in - azimuth_calibration_from[x]) * (azimuth_calibration_to[x+1] - azimuth_calibration_to[x]) / (azimuth_calibration_from[x + 1] - azimuth_calibration_from[x]) + azimuth_calibration_to[x];
    }
  }
  return(azimuth_in);

}
#endif // FEATURE_AZIMUTH_CORRECTION
// --------------------------------------------------------------------------
#ifdef FEATURE_ELEVATION_CORRECTION
float correct_elevation(float elevation_in){


  if (sizeof(elevation_calibration_from) != sizeof(elevation_calibration_to)) {
    return elevation_in;
  }
  for (int x = 0; x < (sizeof(elevation_calibration_from) - 2); x++) {
    if ((elevation_in >= elevation_calibration_from[x]) && (elevation_in <= elevation_calibration_from[x + 1])) {
      // changed this from map() 2015-03-28 due to it blowing up at compile time in Arduino 1.6.1
      return (elevation_in - elevation_calibration_from[x]) * (elevation_calibration_to[x+1] - elevation_calibration_to[x]) / (elevation_calibration_from[x + 1] - elevation_calibration_from[x]) + elevation_calibration_to[x];
    }
  }

  return(elevation_in);


}
#endif // FEATURE_ELEVATION_CORRECTION
// --------------------------------------------------------------------------
#ifdef FEATURE_JOYSTICK_CONTROL
void check_joystick(){

  int joystick_x = 0;
  int joystick_y = 0;

  static int joystick_resting_x = 0;
  static int joystick_resting_y = 0;

  static unsigned long last_joystick_az_action_time = 0;

  static byte joystick_azimuth_rotation = NOT_DOING_ANYTHING;

  #ifdef FEATURE_ELEVATION_CONTROL
  static byte joystick_elevation_rotation = NOT_DOING_ANYTHING;
  static unsigned long last_joystick_el_action_time = 0;
  #endif // FEATURE_ELEVATION_CONTROL

  if ((joystick_resting_x == 0) || (joystick_resting_y == 0)) {  // initialize the resting readings if this is our first time here

    joystick_resting_x = analogReadEnhanced(pin_joystick_x);
    joystick_resting_y = analogReadEnhanced(pin_joystick_y);

  } else {

    joystick_x = analogReadEnhanced(pin_joystick_x);
    joystick_y = analogReadEnhanced(pin_joystick_y);

    if ((millis() - last_joystick_az_action_time) > JOYSTICK_WAIT_TIME_MS) {
      #ifdef DEBUG_JOYSTICK
      static unsigned long last_debug_joystick_status = 0;

      if ((debug_mode) && ((millis() - last_debug_joystick_status) > 1000)) {
        control_port->print("check_joystick: x: ");
        control_port->print(joystick_x);
        control_port->print("\ty: ");
        control_port->println(joystick_y);
        last_debug_joystick_status = millis();
      }
      #endif // DEBUG_JOYSTICK

      #ifndef OPTION_JOYSTICK_REVERSE_X_AXIS
      if ((joystick_resting_x - joystick_x) < (joystick_resting_x * -0.2)) {   // left
      #else
      if ((joystick_resting_x - joystick_x) > (joystick_resting_x * 0.2)) {
      #endif
        #ifdef DEBUG_JOYSTICK
        if (debug_mode) {
          control_port->println("check_joystick: L");
        }
          #endif // DEBUG_JOYSTICK
        if (current_az_state() != ROTATING_CCW) {
          submit_request(AZ, REQUEST_CCW, 0, 1);
        }
        joystick_azimuth_rotation = ROTATING_CCW;
        last_joystick_az_action_time = millis();

      } else {
        #ifndef OPTION_JOYSTICK_REVERSE_X_AXIS
        if ((joystick_resting_x - joystick_x) > (joystick_resting_x * 0.2)) {  // right
        #else
        if ((joystick_resting_x - joystick_x) < (joystick_resting_x * -0.2)) {
        #endif
          #ifdef DEBUG_JOYSTICK
          if (debug_mode) {
            control_port->println("check_joystick: R");
          }
            #endif // DEBUG_JOYSTICK
          if (current_az_state() != ROTATING_CW) {
            submit_request(AZ, REQUEST_CW, 0, 2);
          }
          joystick_azimuth_rotation = ROTATING_CW;
          last_joystick_az_action_time = millis();

        } else { // joystick is in X axis resting position
          if (joystick_azimuth_rotation != NOT_DOING_ANYTHING) {
            if (current_az_state() != NOT_DOING_ANYTHING) {
              submit_request(AZ, REQUEST_STOP, 0, 3);
              last_joystick_az_action_time = millis();
            }
            joystick_azimuth_rotation = NOT_DOING_ANYTHING;
          }
        }

      }

    }

    #ifdef FEATURE_ELEVATION_CONTROL
    if ((millis() - last_joystick_el_action_time) > JOYSTICK_WAIT_TIME_MS) {   
      #ifndef OPTION_JOYSTICK_REVERSE_Y_AXIS
      if ((joystick_resting_y - joystick_y) > (joystick_resting_y * 0.2)) {  // down
        #else
      if ((joystick_resting_y - joystick_y) < (joystick_resting_y * -0.2)) {
          #endif
          #ifdef DEBUG_JOYSTICK
        if (debug_mode) {
          control_port->println("check_joystick: D");
        }
          #endif // DEBUG_JOYSTICK
        if (current_el_state() != ROTATING_DOWN) {
          submit_request(EL, REQUEST_DOWN, 0, 4);
        }
        joystick_elevation_rotation = ROTATING_DOWN;
        last_joystick_el_action_time = millis();
      } else {
            #ifndef OPTION_JOYSTICK_REVERSE_Y_AXIS
        if ((joystick_resting_y - joystick_y) < (joystick_resting_y * -0.2)) { // up
          #else
        if ((joystick_resting_y - joystick_y) > (joystick_resting_y * 0.2)) {
            #endif
            #ifdef DEBUG_JOYSTICK
          if (debug_mode) {
            control_port->println("check_joystick: U");
          }
            #endif // DEBUG_JOYSTICK
          if (current_el_state() != ROTATING_UP) {
            submit_request(EL, REQUEST_UP, 0, 5);
          }
          joystick_elevation_rotation = ROTATING_UP;
          last_joystick_el_action_time = millis();

        } else {  // Y axis is in resting position
          if (joystick_elevation_rotation != NOT_DOING_ANYTHING) {
            if (current_el_state() != NOT_DOING_ANYTHING) {
              submit_request(EL, REQUEST_STOP, 0, 6);
              last_joystick_el_action_time = millis();
            }
            joystick_elevation_rotation = NOT_DOING_ANYTHING;
          }
        }
      }
      

    }
    #endif // FEATURE_ELEVATION_CONTROL

  }


} /* check_joystick */
#endif // FEATURE_JOYSTICK_CONTROL
// --------------------------------------------------------------------------

#ifdef FEATURE_ROTATION_INDICATOR_PIN
void service_rotation_indicator_pin(){


  static byte rotation_indication_pin_state = 0;
  static unsigned long time_rotation_went_inactive = 0;

  #ifdef FEATURE_ELEVATION_CONTROL
  if ((!rotation_indication_pin_state) && ((az_state != IDLE) || (el_state != IDLE))) {
    #else
  if ((!rotation_indication_pin_state) && ((az_state != IDLE))) {
      #endif
    if (rotation_indication_pin) {
      digitalWriteEnhanced(rotation_indication_pin, ROTATION_INDICATOR_PIN_ACTIVE_STATE);
    }
    rotation_indication_pin_state = 1;
      #ifdef DEBUG_ROTATION_INDICATION_PIN
    if (debug_mode) {
      control_port->println(F("service_rotation_indicator_pin: active"));
    }
      #endif
  }

    #ifdef FEATURE_ELEVATION_CONTROL
  if ((rotation_indication_pin_state) && (az_state == IDLE) && (el_state == IDLE)) {
      #else
  if ((rotation_indication_pin_state) && (az_state == IDLE)) {
        #endif
    if (time_rotation_went_inactive == 0) {
      time_rotation_went_inactive = millis();
    } else {
      if ((millis() - time_rotation_went_inactive) >= ((ROTATION_INDICATOR_PIN_TIME_DELAY_SECONDS * 1000) + (ROTATION_INDICATOR_PIN_TIME_DELAY_MINUTES * 60 * 1000))) {
        if (rotation_indication_pin) {
          digitalWriteEnhanced(rotation_indication_pin, ROTATION_INDICATOR_PIN_INACTIVE_STATE);
        }
        rotation_indication_pin_state = 0;
        time_rotation_went_inactive = 0;
              #ifdef DEBUG_ROTATION_INDICATION_PIN
        if (debug_mode) {
          control_port->println(F("service_rotation_indicator_pin: inactive"));
        }
              #endif
      }
    }
  }


} /* service_rotation_indicator_pin */
      #endif // FEATURE_ROTATION_INDICATOR_PIN
// --------------------------------------------------------------
#ifdef FEATURE_PARK
void deactivate_park(){

  park_status = NOT_PARKED;
  park_serial_initiated = 0;
}
#endif // FEATURE_PARK

// --------------------------------------------------------------
#ifdef FEATURE_PARK
void initiate_park(){

  #ifdef DEBUG_PARK
  control_port->println(F("initiate_park: park initiated"));
  #endif // DEBUG_PARK

  byte park_initiated = 0;

  stop_all_tracking();

  if (abs(raw_azimuth - PARK_AZIMUTH) > (AZIMUTH_TOLERANCE * HEADING_MULTIPLIER)) {
    submit_request(AZ, REQUEST_AZIMUTH_RAW, PARK_AZIMUTH, 7);
    park_initiated = 1;
  }
  #ifdef FEATURE_ELEVATION_CONTROL
  if (abs(elevation - PARK_ELEVATION) > (ELEVATION_TOLERANCE * HEADING_MULTIPLIER)) {
    submit_request(EL, REQUEST_ELEVATION, PARK_ELEVATION, 8);
    park_initiated = 1;
  }
  #endif // FEATURE_ELEVATION_CONTROL

  if (park_initiated) {
    park_status = PARK_INITIATED;
  } else {
    park_status = PARKED;
  }

} /* initiate_park */
  #endif // FEATURE_PARK

// --------------------------------------------------------------
#ifdef FEATURE_PARK
void service_park(){

  static byte last_park_status = NOT_PARKED;

  if (park_status == PARKED) {
    if (abs(raw_azimuth - PARK_AZIMUTH) > (AZIMUTH_TOLERANCE * HEADING_MULTIPLIER)) {
      park_status = NOT_PARKED;
    }
    #ifdef FEATURE_ELEVATION_CONTROL
    if (abs(elevation - PARK_ELEVATION) > (ELEVATION_TOLERANCE * HEADING_MULTIPLIER)) {
      park_status = NOT_PARKED;
    }
    #endif // FEATURE_ELEVATION_CONTROL
  }


  if (park_status != last_park_status) {
    switch (park_status) {
      case NOT_PARKED:
        if (park_in_progress_pin) {
          digitalWriteEnhanced(park_in_progress_pin, LOW);
        }
        if (parked_pin) {
          digitalWriteEnhanced(parked_pin, LOW);
        }
      #ifdef DEBUG_PARK
        control_port->println(F("service_park: park_in_progress_pin: LOW  parked_pin: LOW"));
      #endif // DEBUG_PARK
        break;
      case PARK_INITIATED:
        if (park_in_progress_pin) {
          digitalWriteEnhanced(park_in_progress_pin, HIGH);
        }
        if (parked_pin) {
          digitalWriteEnhanced(parked_pin, LOW);
        }
      #ifdef DEBUG_PARK
        control_port->println(F("service_park: park_in_progress_pin: HIGH  parked_pin: LOW"));
      #endif // DEBUG_PARK
        break;
      case PARKED:
        if (park_in_progress_pin) {
          digitalWriteEnhanced(park_in_progress_pin, LOW);
        }
        if (parked_pin) {
          digitalWriteEnhanced(parked_pin, HIGH);
        }
        if (park_serial_initiated) {
        #if defined(FEATURE_REMOTE_UNIT_SLAVE) || defined(FEATURE_YAESU_EMULATION) || defined(FEATURE_EASYCOM_EMULATION)
          control_port->println(F("Parked."));
        #endif
          park_serial_initiated = 0;
        }
      #ifdef DEBUG_PARK
        control_port->println(F("service_park: park_in_progress_pin: LOW  parked_pin: HIGH"));
      #endif // DEBUG_PARK
        break;
    } /* switch */
  }

  last_park_status = park_status;

} /* service_park */
#endif // FEATURE_PARK

// --------------------------------------------------------------

#ifdef FEATURE_LIMIT_SENSE
void check_limit_sense(){

  static byte az_limit_tripped = 0;

  #ifdef FEATURE_ELEVATION_CONTROL
  static byte el_limit_tripped = 0;
  #endif // FEATURE_ELEVATION_CONTROL

  if (az_limit_sense_pin) {
    if (digitalReadEnhanced(az_limit_sense_pin) == 0) {
      if (!az_limit_tripped) {
        submit_request(AZ, REQUEST_KILL, 0, 9);
        az_limit_tripped = 1;
        #ifdef DEBUG_LIMIT_SENSE
        control_port->println(F("check_limit_sense: az limit tripped"));
        #endif // DEBUG_LIMIT_SENSE
      }
    } else {
      az_limit_tripped = 0;
    }
  }

    #ifdef FEATURE_ELEVATION_CONTROL
  if (el_limit_sense_pin) {
    if (digitalReadEnhanced(el_limit_sense_pin) == 0) {
      if (!el_limit_tripped) {
        submit_request(EL, REQUEST_KILL, 0, 10);
        el_limit_tripped = 1;
          #ifdef DEBUG_LIMIT_SENSE
        control_port->println(F("check_limit_sense: el limit tripped"));
          #endif // DEBUG_LIMIT_SENSE
      }
    } else {
      el_limit_tripped = 0;
    }
  }
      #endif // FEATURE_ELEVATION_CONTROL


} /* check_limit_sense */
    #endif // FEATURE_LIMIT_SENSE
// --------------------------------------------------------------
#ifdef FEATURE_AZ_POSITION_INCREMENTAL_ENCODER
void az_position_incremental_encoder_interrupt_handler(){

  byte rotation_result = 0;
  byte current_phase_a = digitalReadEnhanced(az_incremental_encoder_pin_phase_a);
  byte current_phase_b = digitalReadEnhanced(az_incremental_encoder_pin_phase_b);
  byte current_phase_z = digitalReadEnhanced(az_incremental_encoder_pin_phase_z);

  #ifdef DEBUG_AZ_POSITION_INCREMENTAL_ENCODER
  az_position_incremental_encoder_interrupt++;
  #endif // DEBUG_AZ_POSITION_INCREMENTAL_ENCODER

  if ((az_3_phase_encoder_last_phase_a_state != current_phase_a) || (az_3_phase_encoder_last_phase_b_state != current_phase_b)) {
    if (az_3_phase_encoder_last_phase_a_state == LOW) {
      rotation_result++;
    }
    rotation_result = rotation_result << 1;
    if (az_3_phase_encoder_last_phase_b_state == LOW) {
      rotation_result++;
    }
    rotation_result = rotation_result << 1;
    if (current_phase_a == LOW) {
      rotation_result++;
    }
    rotation_result = rotation_result << 1;
    if (current_phase_b == LOW) {
      rotation_result++;
    }
    switch (rotation_result) {
      case B0010: //az_incremental_encoder_position++; break;
      case B1011: //az_incremental_encoder_position++; break;
      case B1101: //az_incremental_encoder_position++; break;
      case B0100: az_incremental_encoder_position++; break;

      case B0001: //az_incremental_encoder_position--; break;
      case B0111: //az_incremental_encoder_position--; break;
      case B1110: //az_incremental_encoder_position--; break;
      case B1000: az_incremental_encoder_position--; break;
    }

    if (az_incremental_encoder_position > ((int(AZ_POSITION_INCREMENTAL_ENCODER_PULSES_PER_REV*4.) - 1) * 2)) {
      az_incremental_encoder_position = 0;
    }
    if (az_incremental_encoder_position < 0) {
      az_incremental_encoder_position = ((int(AZ_POSITION_INCREMENTAL_ENCODER_PULSES_PER_REV*4.) - 1) * 2);
    }

    #ifndef OPTION_SCANCON_2RMHF3600_INC_ENCODER
    if ((current_phase_a == LOW) && (current_phase_b == LOW) && (current_phase_z == LOW)) {
      if ((az_incremental_encoder_position < int((AZ_POSITION_INCREMENTAL_ENCODER_PULSES_PER_REV*4.) / 2)) || (az_incremental_encoder_position > int((AZ_POSITION_INCREMENTAL_ENCODER_PULSES_PER_REV*4.) * 1.5))) {
        az_incremental_encoder_position = AZ_INCREMENTAL_ENCODER_ZERO_PULSE_POSITION;
      } else {
        az_incremental_encoder_position = int(AZ_POSITION_INCREMENTAL_ENCODER_PULSES_PER_REV*4.);
      }
    }
    #else
    if ((current_phase_a == HIGH) && (current_phase_b == HIGH) && (current_phase_z == HIGH)) {
      if ((az_incremental_encoder_position < int((AZ_POSITION_INCREMENTAL_ENCODER_PULSES_PER_REV*4.) / 2)) || (az_incremental_encoder_position > int((AZ_POSITION_INCREMENTAL_ENCODER_PULSES_PER_REV*4.) * 1.5))) {
        az_incremental_encoder_position = AZ_INCREMENTAL_ENCODER_ZERO_PULSE_POSITION;
      } else {
        az_incremental_encoder_position = int(AZ_POSITION_INCREMENTAL_ENCODER_PULSES_PER_REV*4.);
      }
    }    
    #endif //OPTION_SCANCON_2RMHF3600_INC_ENCODER

    az_3_phase_encoder_last_phase_a_state = current_phase_a;
    az_3_phase_encoder_last_phase_b_state = current_phase_b;

  }

  if (!read_azimuth_lock){
    read_azimuth(1);
    if(!service_rotation_lock){
      service_rotation();
    }
  }
  


} /* az_position_incremental_encoder_interrupt_handler */
  #endif // FEATURE_AZ_POSITION_INCREMENTAL_ENCODER
// --------------------------------------------------------------

#if defined(FEATURE_EL_POSITION_INCREMENTAL_ENCODER) && defined(FEATURE_ELEVATION_CONTROL)
void el_position_incremental_encoder_interrupt_handler(){

  byte rotation_result = 0;
  byte current_phase_a = digitalReadEnhanced(el_incremental_encoder_pin_phase_a);
  byte current_phase_b = digitalReadEnhanced(el_incremental_encoder_pin_phase_b);
  byte current_phase_z = digitalReadEnhanced(el_incremental_encoder_pin_phase_z);

  #ifdef DEBUG_EL_POSITION_INCREMENTAL_ENCODER
  el_position_incremental_encoder_interrupt++;
  #endif // DEBUG_EL_POSITION_INCREMENTAL_ENCODER

  if ((el_3_phase_encoder_last_phase_a_state != current_phase_a) || (el_3_phase_encoder_last_phase_b_state != current_phase_b)) {
    if (el_3_phase_encoder_last_phase_a_state == LOW) {
      rotation_result++;
    }
    rotation_result = rotation_result << 1;
    if (el_3_phase_encoder_last_phase_b_state == LOW) {
      rotation_result++;
    }
    rotation_result = rotation_result << 1;
    if (current_phase_a == LOW) {
      rotation_result++;
    }
    rotation_result = rotation_result << 1;
    if (current_phase_b == LOW) {
      rotation_result++;
    }
    switch (rotation_result) {
      case B0010: //el_incremental_encoder_position++; break;
      case B1011: //el_incremental_encoder_position++; break;
      case B1101: //el_incremental_encoder_position++; break;
      case B0100: el_incremental_encoder_position++; break;

      case B0001: //el_incremental_encoder_position--; break;
      case B0111: //el_incremental_encoder_position--; break;
      case B1110: //el_incremental_encoder_position--; break;
      case B1000: el_incremental_encoder_position--; break;
    }

    
    #ifndef OPTION_SCANCON_2RMHF3600_INC_ENCODER
    if ((current_phase_a == LOW) && (current_phase_b == LOW) && (current_phase_z == LOW)) {
      el_incremental_encoder_position = EL_INCREMENTAL_ENCODER_ZERO_PULSE_POSITION;
    } else {

      if (el_incremental_encoder_position < 0) {
        el_incremental_encoder_position = int((EL_POSITION_INCREMENTAL_ENCODER_PULSES_PER_REV*4.) - 1);
      }

      if (el_incremental_encoder_position >= int(EL_POSITION_INCREMENTAL_ENCODER_PULSES_PER_REV*4.)) {
        el_incremental_encoder_position = 0;
      }  

    } 
    #else
    if ((current_phase_a == HIGH) && (current_phase_b == HIGH) && (current_phase_z == HIGH)) {
      el_incremental_encoder_position = EL_INCREMENTAL_ENCODER_ZERO_PULSE_POSITION;
    } else {

      if (el_incremental_encoder_position < 0) {
        el_incremental_encoder_position = int((EL_POSITION_INCREMENTAL_ENCODER_PULSES_PER_REV*4.) - 1);
      }

      if (el_incremental_encoder_position >= int(EL_POSITION_INCREMENTAL_ENCODER_PULSES_PER_REV*4.)) {
        el_incremental_encoder_position = 0;
      }  

    } 
    #endif //OPTION_SCANCON_2RMHF3600_INC_ENCODER

    /*

    if (el_incremental_encoder_position > ((int((EL_POSITION_INCREMENTAL_ENCODER_PULSES_PER_REV*4.)) - 1) * 2)) {
      el_incremental_encoder_position = 0;
    }
    if (el_incremental_encoder_position < 0) {
      el_incremental_encoder_position = ((int(((EL_POSITION_INCREMENTAL_ENCODER_PULSES_PER_REV*4.)*4.)) - 1) * 2);
    }

    if ((current_phase_a == LOW) && (current_phase_b == LOW) && (current_phase_z == LOW)) {
      if ((el_incremental_encoder_position < int((EL_POSITION_INCREMENTAL_ENCODER_PULSES_PER_REV*4.) / 2)) || (el_incremental_encoder_position > int((EL_POSITION_INCREMENTAL_ENCODER_PULSES_PER_REV*4.) * 1.5))) {
        el_incremental_encoder_position = 0;
      } else {
        el_incremental_encoder_position = int((EL_POSITION_INCREMENTAL_ENCODER_PULSES_PER_REV*4.));
      }
    }

    */

    el_3_phase_encoder_last_phase_a_state = current_phase_a;
    el_3_phase_encoder_last_phase_b_state = current_phase_b;

  }

  if (!read_elevation_lock){
    read_elevation(1);
    if(!service_rotation_lock){
      service_rotation();
    }
  }


} /* el_position_incremental_encoder_interrupt_handler */
  #endif // defined(FEATURE_EL_POSITION_INCREMENTAL_ENCODER) && defined(FEATURE_ELEVATION_CONTROL)

// --------------------------------------------------------------

void pinModeEnhanced(uint8_t pin, uint8_t mode){

  #if !defined(FEATURE_MASTER_WITH_SERIAL_SLAVE) && !defined(FEATURE_MASTER_WITH_ETHERNET_SLAVE)
  pinMode(pin, mode);
  #else
  if (pin < 100) {
    pinMode(pin, mode);
  } else {
    submit_remote_command(REMOTE_UNIT_DHL_COMMAND, pin, mode);
  }
  #endif // !defined(FEATURE_MASTER_WITH_SERIAL_SLAVE) && !defined(FEATURE_MASTER_WITH_ETHERNET_SLAVE)

}

// --------------------------------------------------------------

void digitalWriteEnhanced(uint8_t pin, uint8_t writevalue){



  #if !defined(FEATURE_MASTER_WITH_SERIAL_SLAVE) && !defined(FEATURE_MASTER_WITH_ETHERNET_SLAVE)
  digitalWrite(pin, writevalue);
  #else
  if (pin < 100) {
    digitalWrite(pin, writevalue);
  } else {
    submit_remote_command(REMOTE_UNIT_DHL_COMMAND, pin, writevalue);
  }
  #endif // !defined(FEATURE_MASTER_WITH_SERIAL_SLAVE) && !defined(FEATURE_MASTER_WITH_ETHERNET_SLAVE)

}

// --------------------------------------------------------------

int digitalReadEnhanced(uint8_t pin){

  return digitalRead(pin);

}

// --------------------------------------------------------------

int analogReadEnhanced(uint8_t pin){

  #ifdef OPTION_EXTERNAL_ANALOG_REFERENCE
  analogReference(EXTERNAL);
  #endif //OPTION_EXTERNAL_ANALOG_REFERENCE
  return analogRead(pin);

}

// --------------------------------------------------------------


void analogWriteEnhanced(uint8_t pin, int writevalue){


  #if !defined(FEATURE_MASTER_WITH_SERIAL_SLAVE) && !defined(FEATURE_MASTER_WITH_ETHERNET_SLAVE)
  analogWrite(pin, writevalue);
  #else
  if (pin < 100) {
    analogWrite(pin, writevalue);
  } else {
    submit_remote_command(REMOTE_UNIT_AW_COMMAND, pin, writevalue);
  }
  #endif // !defined(FEATURE_MASTER_WITH_SERIAL_SLAVE) && !defined(FEATURE_MASTER_WITH_ETHERNET_SLAVE)

}


// --------------------------------------------------------------

#if defined(FEATURE_MASTER_WITH_SERIAL_SLAVE) || defined(FEATURE_MASTER_WITH_ETHERNET_SLAVE)
void take_care_of_pending_remote_command(){

  // if there's a command already sent to the remote and we're awaiting the response, service the serial buffer and the queue

  unsigned long start_time = millis();

  while ((remote_unit_command_submitted) && ((millis() - start_time) < 200)) {
    #if defined(FEATURE_MASTER_WITH_SERIAL_SLAVE)
    check_serial();
    #endif //defined(FEATURE_MASTER_WITH_SERIAL_SLAVE)
    #if defined(FEATURE_MASTER_WITH_ETHERNET_SLAVE)
    service_ethernet();
    #endif //defined(FEATURE_MASTER_WITH_ETHERNET_SLAVE)
    service_remote_communications_incoming_buffer();
  }


}
#endif // defined(FEATURE_MASTER_WITH_SERIAL_SLAVE) || defined(FEATURE_MASTER_WITH_ETHERNET_SLAVE)
// --------------------------------------------------------------
#ifdef FEATURE_MOON_TRACKING
void service_moon_tracking(){

  static unsigned long last_check = 0;
  static byte moon_tracking_activated_by_activate_line = 0;

  static byte moon_tracking_pin_state = 0;

  if (moon_tracking_active_pin) {
    if ((moon_tracking_active) && (!moon_tracking_pin_state)) {
      digitalWriteEnhanced(moon_tracking_active_pin, HIGH);
      moon_tracking_pin_state = 1;
    }
    if ((!moon_tracking_active) && (moon_tracking_pin_state)) {
      digitalWriteEnhanced(moon_tracking_active_pin, LOW);
      moon_tracking_pin_state = 0;
    }
  }

  if (moon_tracking_activate_line) {
    if ((!moon_tracking_active) && (!digitalReadEnhanced(moon_tracking_activate_line))) {
      moon_tracking_active = 1;
      moon_tracking_activated_by_activate_line = 1;
    }
    if ((moon_tracking_active) && (digitalReadEnhanced(moon_tracking_activate_line)) && (moon_tracking_activated_by_activate_line)) {
      moon_tracking_active = 0;
      moon_tracking_activated_by_activate_line = 0;
    }
  }

  if ((moon_tracking_active) && ((millis() - last_check) > MOON_TRACKING_CHECK_INTERVAL)) {

    update_time();
    update_moon_position();

    #ifdef DEBUG_MOON_TRACKING
    control_port->print(F("service_moon_tracking: AZ: "));
    control_port->print(moon_azimuth);
    control_port->print(" EL: ");
    control_port->print(moon_elevation);
    control_port->print(" lat: ");
    control_port->print(latitude);
    control_port->print(" long: ");
    control_port->println(longitude);
    #endif // DEBUG_MOON_TRACKING

    if ((moon_azimuth >= MOON_AOS_AZIMUTH_MIN) && (moon_azimuth <= MOON_AOS_AZIMUTH_MAX) && (moon_elevation >= MOON_AOS_ELEVATION_MIN) && (moon_elevation <= MOON_AOS_ELEVATION_MAX)) {
      submit_request(AZ, REQUEST_AZIMUTH, moon_azimuth * HEADING_MULTIPLIER, 11);
      submit_request(EL, REQUEST_ELEVATION, moon_elevation * HEADING_MULTIPLIER, 12);
      if (!moon_visible) {
        moon_visible = 1;
        #ifdef DEBUG_MOON_TRACKING
        control_port->println(F("service_moon_tracking: moon AOS"));
        #endif // DEBUG_MOON_TRACKING
      }
    } else {
      if (moon_visible) {
        moon_visible = 0;
          #ifdef DEBUG_MOON_TRACKING
        control_port->println(F("service_moon_tracking: moon loss of AOS"));
          #endif // DEBUG_MOON_TRACKING
      } else {
            #ifdef DEBUG_MOON_TRACKING
        control_port->println(F("service_moon_tracking: moon out of AOS limits"));
            #endif // DEBUG_MOON_TRACKING
      }
    }

    last_check = millis();
  }



} /* service_moon_tracking */
    #endif // FEATURE_MOON_TRACKING

// --------------------------------------------------------------
#ifdef FEATURE_SUN_TRACKING

void update_sun_position(){

  update_time();
  c_time.iYear = clock_years;
  c_time.iMonth = clock_months;
  c_time.iDay = clock_days;

  c_time.dHours = clock_hours;
  c_time.dMinutes = clock_minutes;
  c_time.dSeconds = clock_seconds;

  c_loc.dLongitude = longitude;
  c_loc.dLatitude  = latitude;

  c_sposn.dZenithAngle = 0;
  c_sposn.dAzimuth = 0;

  sunpos(c_time, c_loc, &c_sposn);

  // Convert Zenith angle to elevation
  sun_elevation = 90. - c_sposn.dZenithAngle;
  sun_azimuth = c_sposn.dAzimuth;

} /* update_sun_position */
#endif // FEATURE_SUN_TRACKING

// --------------------------------------------------------------
#ifdef FEATURE_SUN_TRACKING
void service_sun_tracking(){

  static unsigned long last_check = 0;
  static byte sun_tracking_pin_state = 0;
  static byte sun_tracking_activated_by_activate_line = 0;

  if (sun_tracking_active_pin) {
    if ((sun_tracking_active) && (!sun_tracking_pin_state)) {
      digitalWriteEnhanced(sun_tracking_active_pin, HIGH);
      sun_tracking_pin_state = 1;
    }
    if ((!sun_tracking_active) && (sun_tracking_pin_state)) {
      digitalWriteEnhanced(sun_tracking_active_pin, LOW);
      sun_tracking_pin_state = 0;
    }
  }

  if (sun_tracking_activate_line) {
    if ((!sun_tracking_active) && (!digitalReadEnhanced(sun_tracking_activate_line))) {
      sun_tracking_active = 1;
      sun_tracking_activated_by_activate_line = 1;
    }
    if ((sun_tracking_active) && (digitalReadEnhanced(sun_tracking_activate_line)) && (sun_tracking_activated_by_activate_line)) {
      sun_tracking_active = 0;
      sun_tracking_activated_by_activate_line = 0;
    }
  }

  if ((sun_tracking_active) && ((millis() - last_check) > SUN_TRACKING_CHECK_INTERVAL)) {

    update_time();
    update_sun_position();


    #ifdef DEBUG_SUN_TRACKING
    control_port->print(F("service_sun_tracking: AZ: "));
    control_port->print(sun_azimuth);
    control_port->print(" EL: ");
    control_port->print(sun_elevation);
    control_port->print(" lat: ");
    control_port->print(latitude);
    control_port->print(" long: ");
    control_port->println(longitude);
    #endif // DEBUG_SUN_TRACKING

    if ((sun_azimuth >= SUN_AOS_AZIMUTH_MIN) && (sun_azimuth <= SUN_AOS_AZIMUTH_MAX) && (sun_elevation >= SUN_AOS_ELEVATION_MIN) && (sun_elevation <= SUN_AOS_ELEVATION_MAX)) {
      submit_request(AZ, REQUEST_AZIMUTH, sun_azimuth * HEADING_MULTIPLIER, 13);
      submit_request(EL, REQUEST_ELEVATION, sun_elevation * HEADING_MULTIPLIER, 14);
      if (!sun_visible) {
        sun_visible = 1;
        #ifdef DEBUG_SUN_TRACKING
        control_port->println(F("service_sun_tracking: sun AOS"));
        #endif // DEBUG_SUN_TRACKING
      }
    } else {
      if (sun_visible) {
        sun_visible = 0;
          #ifdef DEBUG_SUN_TRACKING
        control_port->println(F("service_sun_tracking: sun loss of AOS"));
          #endif // DEBUG_SUN_TRACKING
      } else {
            #ifdef DEBUG_SUN_TRACKING
        control_port->println(F("service_sun_tracking: sun out of AOS limits"));
            #endif // DEBUG_SUN_TRACKING
      }
    }

    last_check = millis();
  }




} /* service_sun_tracking */
#endif // FEATURE_SUN_TRACKING
// --------------------------------------------------------------

#ifdef FEATURE_CLOCK
void update_time(){
  unsigned long runtime = millis() - millis_at_last_calibration;

  unsigned long time = (3600L * clock_hour_set) + (60L * clock_min_set) + clock_sec_set + ((runtime + (runtime * INTERNAL_CLOCK_CORRECTION)) / 1000.0);

  clock_years = clock_year_set;
  clock_months = clock_month_set;
  clock_days = time / 86400L;
  time -= clock_days * 86400L;
  clock_days += clock_day_set;
  clock_hours = time / 3600L;

  switch (clock_months) {

    case 1:
    case 3:
    case 5:
    case 7:
    case 8:
    case 10:
    case 12:
      if (clock_days > 31) {
        clock_days = 1; clock_months++;
      }
      break;

    case 2:
      if ((float(clock_years) / 4.0) == 0.0) {  // do we have a leap year?
        if (clock_days > 29) {
          clock_days = 1; clock_months++;
        }
      } else {
        if (clock_days > 28) {
          clock_days = 1; clock_months++;
        }
      }
      break;

    case 4:
    case 6:
    case 9:
    case 11:
      if (clock_days > 30) {
        clock_days = 1; clock_months++;
      }
      break;
  } /* switch */

  if (clock_months > 12) {
    clock_months = 1; clock_years++;
  }

  time -= clock_hours * 3600L;
  clock_minutes  = time / 60L;
  time -= clock_minutes * 60L;
  clock_seconds = time;
} /* update_time */
    #endif // FEATURE_CLOCK


// --------------------------------------------------------------
#ifdef FEATURE_GPS
void service_gps(){

  long gps_lat, gps_lon;
  unsigned long fix_age;
  int gps_year;
  byte gps_month, gps_day, gps_hours, gps_minutes, gps_seconds, gps_hundredths;
  byte gps_sync_pin_active = 0;
  #ifdef DEBUG_GPS
  char tempstring[10] = "";
  #endif //#ifdef DEBUG_GPS

  static unsigned long last_sync = 0;

  if (gps_data_available) {
    // retrieves +/- lat/long in 100000ths of a degree
    gps.get_position(&gps_lat, &gps_lon, &fix_age);
    gps.crack_datetime(&gps_year, &gps_month, &gps_day, &gps_hours, &gps_minutes, &gps_seconds, &gps_hundredths, &fix_age);
    #ifdef DEBUG_GPS
    #ifdef DEBUG_GPS_SERIAL
    debug_println("");
    #endif //DEBUG_GPS_SERIAL    
    debug_print("service_gps: fix_age:");
    debug_print_int(fix_age);
    debug_print(" lat:");
    debug_print_float(gps_lat,4);
    debug_print(" long:");
    debug_print_float(gps_lon,4);
    debug_print(" ");
    debug_print_int(gps_year);
    debug_print("-");
    debug_print_int(gps_month);
    debug_print("-");
    debug_print_int(gps_day);
    debug_print(" ");
    debug_print_int(gps_hours);
    debug_print(":");
    debug_print_int(gps_minutes);
    debug_println("");
    #endif // DEBUG_GPS

    if (fix_age < GPS_VALID_FIX_AGE_MS) {

      if (SYNC_TIME_WITH_GPS) {
        clock_year_set = gps_year;
        clock_month_set = gps_month;
        clock_day_set = gps_day;
        clock_hour_set = gps_hours;
        clock_min_set = gps_minutes;
        clock_sec_set = gps_seconds;
        millis_at_last_calibration = millis() - GPS_UPDATE_LATENCY_COMPENSATION_MS;
        update_time();
        #ifdef DEBUG_GPS
        #ifdef DEBUG_GPS_SERIAL
        debug_println("");
        #endif //DEBUG_GPS_SERIAL        
        debug_print("service_gps: clock sync:");
        sprintf(tempstring,"%s",clock_string());
        debug_print(tempstring);
        debug_println("");
        #endif // DEBUG_GPS
      }

      #if defined(OPTION_SYNC_RTC_TO_GPS) && defined(FEATURE_RTC_DS1307)
      static unsigned long last_rtc_gps_sync_time;
      if ((millis() - last_rtc_gps_sync_time) >= (SYNC_RTC_TO_GPS_SECONDS * 1000)) {
        rtc.adjust(DateTime(gps_year, gps_month, gps_day, gps_hours, gps_minutes, gps_seconds));
        #ifdef DEBUG_RTC
        debug_println("service_gps: synced RTC");
        #endif // DEBUG_RTC
        last_rtc_gps_sync_time = millis();
      }
      #endif // defined(OPTION_SYNC_RTC_TO_GPS) && defined(FEATURE_RTC_DS1307)

      #if defined(OPTION_SYNC_RTC_TO_GPS) && defined(FEATURE_RTC_PCF8583)
      static unsigned long last_rtc_gps_sync_time;
      if ((millis() - last_rtc_gps_sync_time) >= (SYNC_RTC_TO_GPS_SECONDS * 1000)) {
        rtc.year = gps_year;
        rtc.month = gps_month;
        rtc.day = gps_day;
        rtc.hour  = gps_hours;
        rtc.minute = gps_minutes;
        rtc.second = gps_seconds;
        rtc.set_time();
        #ifdef DEBUG_RTC
        debug_println("service_gps: synced RTC");
        #endif // DEBUG_RTC
        last_rtc_gps_sync_time = millis();
      }
      #endif // defined(OPTION_SYNC_RTC_TO_GPS) && defined(FEATURE_RTC_PCF8583)


      #if defined(FEATURE_MOON_TRACKING) || defined(FEATURE_SUN_TRACKING) || defined(FEATURE_REMOTE_UNIT_SLAVE)
      if (SYNC_COORDINATES_WITH_GPS) {
        latitude = float(gps_lat) / 1000000.0;
        longitude = float(gps_lon) / 1000000.0;
        #ifdef DEBUG_GPS
        debug_print("service_gps: coord sync:");
        debug_print_float(latitude,2);
        debug_print(" ");
        debug_print_float(longitude,2);
        debug_println("");
        #endif // DEBUG_GPS
      }
      #endif // defined(FEATURE_MOON_TRACKING) || defined(FEATURE_SUN_TRACKING)

      last_sync = millis();
    }

    gps_data_available = 0;
  }

  if ((millis() > (GPS_SYNC_PERIOD_SECONDS * 1000)) && ((millis() - last_sync) < (GPS_SYNC_PERIOD_SECONDS * 1000)) && (SYNC_TIME_WITH_GPS)) {
    clock_status = GPS_SYNC;
    if ((!gps_sync_pin_active) && (gps_sync)){
      digitalWriteEnhanced(gps_sync,HIGH);
      gps_sync_pin_active = 1;  
    }
  } else {
    if (clock_status == GPS_SYNC) {
      clock_status = FREE_RUNNING;
      if (gps_sync_pin_active){
        digitalWriteEnhanced(gps_sync,LOW);
        gps_sync_pin_active = 0;  
      }
    }
  }


} /* service_gps */
  #endif // FEATURE_GPS
// --------------------------------------------------------------
#ifdef FEATURE_MOON_TRACKING

void update_moon_position(){

  update_time();

  double RA, Dec, topRA, topDec, LST, HA, dist;
  update_time();
  moon2(clock_years, clock_months, clock_days, (clock_hours + (clock_minutes / 60.0) + (clock_seconds / 3600.0)), longitude, latitude, &RA, &Dec, &topRA, &topDec, &LST, &HA, &moon_azimuth, &moon_elevation, &dist);


}
#endif // FEATURE_SUN_TRACKING
// --------------------------------------------------------------
#if defined(FEATURE_MOON_TRACKING) || defined(FEATURE_SUN_TRACKING)
byte calibrate_az_el(float new_az, float new_el){

  #ifdef DEBUG_OFFSET
  control_port->print("calibrate_az_el: new_az:");
  control_port->print(new_az, 2);
  control_port->print(" new_el:");
  control_port->println(new_el, 2);
  #endif // DEBUG_OFFSET



  if ((new_az >= 0 ) && (new_az <= 360) && (new_el >= 0) && (new_el <= 90)) {
    configuration.azimuth_offset = 0;
    configuration.elevation_offset = 0;
    read_azimuth(1);
    read_elevation(1);

    #ifdef DEBUG_OFFSET
    control_port->print("calibrate_az_el: az:");
    control_port->print(azimuth / LCD_HEADING_MULTIPLIER, 2);
    control_port->print(" el:");
    control_port->println(elevation / LCD_HEADING_MULTIPLIER, 2);
    #endif // DEBUG_OFFSET


    configuration.azimuth_offset = new_az - (float(raw_azimuth) / float(HEADING_MULTIPLIER));
    #if defined(FEATURE_ELEVATION_CONTROL)
    configuration.elevation_offset = new_el - (float(elevation) / float(HEADING_MULTIPLIER));
    #endif
    configuration_dirty = 1;
    return 1;
  } else {
    return 0;
  }

} /* calibrate_az_el */
  #endif // defined(FEATURE_MOON_TRACKING) || defined(FEATURE_SUN_TRACKING)
// --------------------------------------------------------------

#ifdef FEATURE_RTC
void service_rtc(){

  static unsigned long last_rtc_sync_time = 0;

  if (((millis() - last_rtc_sync_time) >= (SYNC_WITH_RTC_SECONDS * 1000)) || (clock_status == FREE_RUNNING)){
    last_rtc_sync_time = millis();
    #ifdef FEATURE_GPS
    if (clock_status == GPS_SYNC) { // if we're also equipped with GPS and we're synced to it, don't sync to realtime clock
      #ifdef DEBUG_RTC
      debug_println("service_rtc: synced to GPS already.  Exiting.");
      #endif // DEBUG_RTC
      return;
    }
    #endif // FEATURE_GPS


    #ifdef FEATURE_RTC_DS1307
    if (rtc.isrunning()) {
      DateTime now = rtc.now();
      #ifdef DEBUG_RTC
      debug_print("service_rtc: syncing: ");
      debug_print_int(now.year());
      debug_print("/");
      debug_print_int(now.month());
      debug_print("/");
      debug_print_int(now.day());
      debug_print(" ");
      debug_print_int(now.hour());
      debug_print(":");
      debug_print_int(now.minute());
      debug_print(":");
      debug_print_int(now.second());
      debug_println("");
      #endif // DEBUG_RTC
      clock_year_set = now.year();
      clock_month_set = now.month();
      clock_day_set = now.day();
      clock_hour_set = now.hour();
      clock_min_set = now.minute();
      clock_sec_set = now.second();
      millis_at_last_calibration = millis();
      update_time();
      clock_status = RTC_SYNC;
    } else {
      clock_status = FREE_RUNNING;
      #ifdef DEBUG_RTC
      debug_println("service_rtc: error: RTC not running");
      #endif // DEBUG_RTC
    }
    #endif //#FEATURE_RTC_DS1307



    #ifdef FEATURE_RTC_PCF8583
    rtc.get_time();
    if ((rtc.year > 2000) && (rtc.month > 0) && (rtc.month < 13)){  // do we have a halfway reasonable date?
      #ifdef DEBUG_RTC
      control_port->print("service_rtc: syncing: ");
      control_port->print(rtc.year, DEC);
      control_port->print('/');
      control_port->print(rtc.month, DEC);
      control_port->print('/');
      control_port->print(rtc.day, DEC);
      control_port->print(' ');
      control_port->print(rtc.hour, DEC);
      control_port->print(':');
      control_port->print(rtc.minute, DEC);
      control_port->print(':');
      control_port->println(rtc.second, DEC);
      #endif // DEBUG_RTC
      clock_year_set = rtc.year;
      clock_month_set = rtc.month;
      clock_day_set = rtc.day;
      clock_hour_set = rtc.hour;
      clock_min_set = rtc.minute;
      clock_sec_set = rtc.second;
      millis_at_last_calibration = millis();
      update_time();
      clock_status = RTC_SYNC;
    } else {
      clock_status = FREE_RUNNING;
      #ifdef DEBUG_RTC
      control_port->print("service_rtc: error: RTC not returning valid date or time: ");
      control_port->print(rtc.year, DEC);
      control_port->print('/');
      control_port->print(rtc.month, DEC);
      control_port->print('/');
      control_port->print(rtc.day, DEC);
      control_port->print(' ');
      control_port->print(rtc.hour, DEC);
      control_port->print(':');
      control_port->print(rtc.minute, DEC);
      control_port->print(':');
      control_port->println(rtc.second, DEC);
      #endif // DEBUG_RTC
    }
    #endif //#FEATURE_RTC_PCF8583



  }
} /* service_rtc */
#endif // FEATURE_RTC

// -------------------------------------------------------------

byte process_backslash_command(byte input_buffer[], int input_buffer_index, byte source_port, char * return_string){

  strcpy(return_string,"");
  static unsigned long serial_led_time = 0;
  float tempfloat = 0;

  #if !defined(FEATURE_AZ_POSITION_ROTARY_ENCODER) && !defined(FEATURE_AZ_POSITION_PULSE_INPUT)
  long place_multiplier = 0;
  byte decimalplace = 0;
  #endif

  #ifdef FEATURE_CLOCK
  int temp_year = 0;
  byte temp_month = 0;
  byte temp_day = 0;
  byte temp_minute = 0;
  byte temp_hour = 0;
  #endif // FEATURE_CLOCK

  #if defined(FEATURE_MOON_TRACKING) || defined(FEATURE_SUN_TRACKING)
  char grid[10] = "";
  byte hit_error = 0;
  #endif // defined(FEATURE_MOON_TRACKING) || defined(FEATURE_SUN_TRACKING)

  #if defined(FEATURE_AZ_POSITION_ROTARY_ENCODER) || defined(FEATURE_AZ_POSITION_PULSE_INPUT)
  int new_azimuth = 9999;
  #endif
  #ifdef FEATURE_ELEVATION_CONTROL
  #if defined(FEATURE_EL_POSITION_ROTARY_ENCODER) || defined(FEATURE_EL_POSITION_PULSE_INPUT)
  int new_elevation = 9999;
  #endif // FEATURE_ELEVATION_CONTROL
  #endif // defined(FEATURE_AZ_POSITION_ROTARY_ENCODER) || defined(FEATURE_AZ_POSITION_PULSE_INPUT)

  char temp_string[20] = "";

  switch (input_buffer[1]) {

   #if defined(FEATURE_AZ_POSITION_ROTARY_ENCODER) || defined(FEATURE_AZ_POSITION_PULSE_INPUT)
    case 'A':      // \Ax[x][x] - manually set azimuth
      new_azimuth = 9999;
      switch (input_buffer_index) {
        case 3:
          new_azimuth = (input_buffer[2] - 48);
          break;
        case 4:
          new_azimuth = ((input_buffer[2] - 48) * 10) + (input_buffer[3] - 48);
          break;
        case 5:
          new_azimuth = ((input_buffer[2] - 48) * 100) + ((input_buffer[3] - 48) * 10) + (input_buffer[4] - 48);
          break;
      }
      if ((new_azimuth >= 0) && (new_azimuth < 360)) {
        azimuth = new_azimuth * HEADING_MULTIPLIER;
        configuration.last_azimuth = new_azimuth;
        raw_azimuth = new_azimuth * HEADING_MULTIPLIER;
        configuration_dirty = 1;
        strcpy(return_string, "Azimuth set to ");
        dtostrf(new_azimuth, 0, 0, temp_string);
        strcat(return_string, temp_string);
      } else {
        strcpy(return_string, "Error.  Format: \\Ax[x][x] ");
      }
      break;
        #else // defined(FEATURE_AZ_POSITION_ROTARY_ENCODER) || defined(FEATURE_AZ_POSITION_PULSE_INPUT)
    case 'A':      // \Ax[xxx][.][xxxx] - manually set azimuth
      place_multiplier = 1;
      for (int x = input_buffer_index - 1; x > 1; x--) {
        if (char(input_buffer[x]) != '.') {
          tempfloat += (input_buffer[x] - 48) * place_multiplier;
          place_multiplier = place_multiplier * 10;
        } else {
          decimalplace = x;
        }
      }
      if (decimalplace) {
        tempfloat = tempfloat / pow(10, (input_buffer_index - decimalplace - 1));
      }
      if ((tempfloat >= 0) && (tempfloat <= 360)) {
        configuration.azimuth_offset = 0;
        read_azimuth(1);
        configuration.azimuth_offset = tempfloat - float(raw_azimuth / HEADING_MULTIPLIER);
        configuration_dirty = 1;
        strcpy(return_string, "Azimuth calibrated to ");
        dtostrf(tempfloat, 0, 2, temp_string);
        strcat(return_string, temp_string);
      } else {
        strcpy(return_string, "Error.");
      }

      break;
      #endif // defined(FEATURE_AZ_POSITION_ROTARY_ENCODER) || defined(FEATURE_AZ_POSITION_PULSE_INPUT)


    #if defined(FEATURE_ELEVATION_CONTROL)
    #if defined(FEATURE_EL_POSITION_ROTARY_ENCODER) || defined(FEATURE_EL_POSITION_PULSE_INPUT)
    case 'B':      // \Ax[x][x] - manually set elevation
      new_elevation = 9999;
      switch (input_buffer_index) {
        case 3:
          new_elevation = (input_buffer[2] - 48);
          break;
        case 4:
          new_elevation = ((input_buffer[2] - 48) * 10) + (input_buffer[3] - 48);
          break;
        case 5:
          new_elevation = ((input_buffer[2] - 48) * 100) + ((input_buffer[3] - 48) * 10) + (input_buffer[4] - 48);
          break;
      }
      if ((new_elevation >= 0) && (new_elevation <= 180)) {
        elevation = new_elevation * HEADING_MULTIPLIER;
        configuration.last_elevation = new_elevation;
        configuration_dirty = 1;
        strcpy(return_string, "Elevation set to ");
        dtostrf(new_elevation, 0, 0, temp_string);
        strcat(return_string, temp_string);
      } else {
        strcpy(return_string, "Error.  Format: \\Bx[x][x]");
      }
      break;
      #else // defined(FEATURE_EL_POSITION_ROTARY_ENCODER) || defined(FEATURE_EL_POSITION_PULSE_INPUT)
    case 'B':      // \Bx[xxx][.][xxxx] - manually set elevation
      place_multiplier = 1;
      for (int x = input_buffer_index - 1; x > 1; x--) {
        if (char(input_buffer[x]) != '.') {
          tempfloat += (input_buffer[x] - 48) * place_multiplier;
          place_multiplier = place_multiplier * 10;
        } else {
          decimalplace = x;
        }
      }
      if (decimalplace) {
        tempfloat = tempfloat / pow(10, (input_buffer_index - decimalplace - 1));
      }
      if ((tempfloat >= 0) && (tempfloat <= 180)) {
        configuration.elevation_offset = 0;
        read_elevation(1);
        configuration.elevation_offset = tempfloat - float(elevation / HEADING_MULTIPLIER);
        configuration_dirty = 1;
        strcpy(return_string, "Elevation calibrated to ");
        dtostrf(tempfloat, 0, 2, temp_string);
        strcat(return_string, temp_string);
      } else {
        strcpy(return_string, "Error.");
      }
      break;
      #endif // defined(FEATURE_EL_POSITION_ROTARY_ENCODER) || defined(FEATURE_EL_POSITION_PULSE_INPUT)
      #endif //FEATURE_ELEVATION_CONTROL

    #ifdef FEATURE_CLOCK
    case 'C':         // show clock
      update_time();
      sprintf(return_string, "%s", clock_string());


      break;
    case 'O':         // set clock
      temp_year = ((input_buffer[2] - 48) * 1000) + ((input_buffer[3] - 48) * 100) + ((input_buffer[4] - 48) * 10) + (input_buffer[5] - 48);
      temp_month = ((input_buffer[6] - 48) * 10) + (input_buffer[7] - 48);
      temp_day = ((input_buffer[8] - 48) * 10) + (input_buffer[9] - 48);
      temp_hour = ((input_buffer[10] - 48) * 10) + (input_buffer[11] - 48);
      temp_minute = ((input_buffer[12] - 48) * 10) + (input_buffer[13] - 48);
      if ((temp_year > 2013) && (temp_year < 2070) &&
          (temp_month > 0) && (temp_month < 13) &&
          (temp_day > 0) && (temp_day < 32) &&
          (temp_hour >= 0) && (temp_hour < 24) &&
          (temp_minute >= 0) && (temp_minute < 60) &&
          (input_buffer_index == 14)) {

        clock_year_set = temp_year;
        clock_month_set = temp_month;
        clock_day_set = temp_day;
        clock_hour_set = temp_hour;
        clock_min_set = temp_minute;
        clock_sec_set = 0;
        millis_at_last_calibration = millis();





        #if defined(FEATURE_RTC_DS1307)
        rtc.adjust(DateTime(temp_year, temp_month, temp_day, temp_hour, temp_minute, 0));
        #endif // defined(FEATURE_RTC_DS1307)
        #if defined(FEATURE_RTC_PCF8583)
        rtc.year = temp_year;
        rtc.month = temp_month;
        rtc.day = temp_day;
        rtc.hour  = temp_hour;
        rtc.minute = temp_minute;
        rtc.second = 0;
        rtc.set_time();
        #endif // defined(FEATURE_RTC_PCF8583)

        #if (!defined(FEATURE_RTC_DS1307) && !defined(FEATURE_RTC_PCF8583))
        strcpy(return_string, "Clock set to ");
        update_time();
        strcat(return_string, clock_string());
        #else
        strcpy(return_string, "Internal clock and RTC set to ");
        update_time();
        strcat(return_string, clock_string());
        #endif


      } else {
        strcpy(return_string, "Error. Usage: \\OYYYYMMDDHHmm");
      }
      break;
          #endif // FEATURE_CLOCK


    case 'D': 
      if (debug_mode & source_port) {
        debug_mode = debug_mode & (~source_port);
      } else {
        debug_mode = debug_mode | source_port;
      } 
      break;                                              // D - Debug

    case 'E':                                                                      // E - Initialize eeprom
      initialize_eeprom_with_defaults();
      strcpy(return_string, "Initialized eeprom, resetting unit in 5 seconds...");
      reset_the_unit = 1;
      break;

    case 'L':                                                                      // L - rotate to long path
      if (azimuth < (180 * HEADING_MULTIPLIER)) {
        submit_request(AZ, REQUEST_AZIMUTH, (azimuth + (180 * HEADING_MULTIPLIER)), 15);
      } else {
        submit_request(AZ, REQUEST_AZIMUTH, (azimuth - (180 * HEADING_MULTIPLIER)), 16);
      }
      break;

 #if defined(FEATURE_MOON_TRACKING) || defined(FEATURE_SUN_TRACKING)
    case 'G':   // G - set coordinates using grid square
      if (isalpha(input_buffer[2])) {
        grid[0] = input_buffer[2];
      } else { hit_error = 1; }
      if (isalpha(input_buffer[3])) {
        grid[1] = input_buffer[3];
      } else { hit_error = 1; }
      if (isdigit(input_buffer[4])) {
        grid[2] = input_buffer[4];
      } else { hit_error = 1; }
      if (isdigit(input_buffer[5])) {
        grid[3] = input_buffer[5];
      } else { hit_error = 1; }
      if (isalpha(input_buffer[6])) {
        grid[4] = input_buffer[6];
      } else { hit_error = 1; }
      if (isalpha(input_buffer[7])) {
        grid[5] = input_buffer[7];
      } else { hit_error = 1; }
      if ((input_buffer_index != 8) || (hit_error)) {
        strcpy(return_string, "Error.  Usage \\Gxxxxxx");
      } else {
        grid2deg(grid, &longitude, &latitude);
        strcpy(return_string, "Coordinates set to: ");
        dtostrf(latitude, 0, 4, temp_string);
        strcat(return_string, temp_string);
        strcat(return_string, " ");
        dtostrf(longitude, 0, 4, temp_string);
        strcat(return_string, temp_string);
      }
      break;
 #endif // defined(FEATURE_MOON_TRACKING) || defined(FEATURE_SUN_TRACKING)

  #ifdef FEATURE_MOON_TRACKING
    case 'M':
      switch (input_buffer[2]) {
        case '0':
          submit_request(AZ, REQUEST_STOP, 0, 17);
          submit_request(EL, REQUEST_STOP, 0, 18);
          strcpy(return_string, "Moon tracking deactivated.");
          break;
        case '1':
          moon_tracking_active = 1;
  #ifdef FEATURE_SUN_TRACKING
          sun_tracking_active = 0;
  #endif // FEATURE_SUN_TRACKING
          strcpy(return_string, "Moon tracking activated.");
          break;
        default: strcpy(return_string, "Error."); break;
      }
      break;
  #endif // FEATURE_MOON_TRACKING

  #if defined(FEATURE_MASTER_WITH_SERIAL_SLAVE) || defined(FEATURE_MASTER_WITH_ETHERNET_SLAVE)
    case 'R':
      strcpy(return_string, "Remote port rx sniff o");
      if (remote_port_rx_sniff) {
        remote_port_rx_sniff = 0;
        strcat(return_string, "ff");
      } else {
        remote_port_rx_sniff = 1;
        strcat(return_string, "n");
      }
      break;
    case 'S':
      #if defined(FEATURE_MASTER_WITH_ETHERNET_SLAVE)
      ethernetslavelinkclient0.print(ETHERNET_PREAMBLE);
      #endif    
      for (int x = 2; x < input_buffer_index; x++) {
        #if defined(FEATURE_MASTER_WITH_SERIAL_SLAVE)
        remote_unit_port->write(input_buffer[x]);
        #endif
        #if defined(FEATURE_MASTER_WITH_ETHERNET_SLAVE)
        ethernetslavelinkclient0.write(input_buffer[x]);
        #endif
        if (remote_port_tx_sniff) {
          control_port->write(input_buffer[x]);
        }
      }
      #if defined(FEATURE_MASTER_WITH_SERIAL_SLAVE)
      remote_unit_port->write(13);
      #endif
      #if defined(FEATURE_MASTER_WITH_ETHERNET_SLAVE)
      ethernetslavelinkclient0.write(13);
      #endif      
      if (remote_port_tx_sniff) {
        control_port->write(13);
      }
      break;
    case 'T':
      strcpy(return_string, "Remote port tx sniff o");
      if (remote_port_tx_sniff) {
        remote_port_tx_sniff = 0;
        strcat(return_string, "ff");
      } else {
        remote_port_tx_sniff = 1;
        strcat(return_string, "n");
      }
      break;
    case 'Z':
      strcpy(return_string, "Suspend auto remote commands o");
      if (suspend_remote_commands) {
        suspend_remote_commands = 0;
        strcat(return_string, "ff");
      } else {
        suspend_remote_commands = 1;
        strcat(return_string, "n");
      }
      break;
  #endif // defined(FEATURE_MASTER_WITH_SERIAL_SLAVE) || defined(FEATURE_MASTER_WITH_ETHERNET_SLAVE)

  #ifdef FEATURE_SUN_TRACKING
    case 'U':     // activate / deactivate sun tracking
      switch (input_buffer[2]) {
        case '0':
          submit_request(AZ, REQUEST_STOP, 0, 19);
          submit_request(EL, REQUEST_STOP, 0, 20);
          strcpy(return_string, "Sun tracking deactivated.");
          break;
        case '1':
          sun_tracking_active = 1;
          strcpy(return_string, "Sun tracking activated.");
          #ifdef FEATURE_MOON_TRACKING
          moon_tracking_active = 0;
          #endif // FEATURE_MOON_TRACKING
          break;
        default: strcpy(return_string, "Error."); break;
      }
      break;

  #endif // FEATURE_SUN_TRACKING

  #if defined(FEATURE_SUN_TRACKING) || defined(FEATURE_MOON_TRACKING)
    case 'X':
      switch (toupper(input_buffer[2])) {
        #if defined(FEATURE_SUN_TRACKING)
        case 'S': //YYYYYYYY
          update_sun_position();
          if (calibrate_az_el(sun_azimuth, sun_elevation)) {
            strcpy(return_string, az_el_calibrated_string());
          } else {
            strcpy(return_string, "Error.");
          }
          break;
        #endif // FEATURE_SUN_TRACKING
        #if defined(FEATURE_MOON_TRACKING)
        case 'M':
          update_moon_position();
          if (calibrate_az_el(moon_azimuth, moon_elevation)) {
            strcpy(return_string, az_el_calibrated_string());
          } else {
            strcpy(return_string, "Error.");
          }
          break;
        #endif // FEATURE_MOON_TRACKING
        case '0':
          configuration.azimuth_offset = 0;
          configuration.elevation_offset = 0;
          configuration_dirty = 1;
          break;
        default: strcpy(return_string, "?>"); break;


      } /* switch */
      break;
      #endif // defined(FEATURE_SUN_TRACKING) || defined(FEATURE_MOON_TRACKING)

    #ifdef FEATURE_PARK
    case 'P':    // Park
      strcpy(return_string, "Parking...");
      initiate_park();
      park_serial_initiated = 1;
      break;
      #endif // FEATURE_PARK

    #ifdef FEATURE_ANCILLARY_PIN_CONTROL
    case 'N':      // \Nxx - turn pin on; xx = pin number
      if ((((input_buffer[2] > 47) && (input_buffer[2] < 58)) || (toupper(input_buffer[2]) == 'A')) && (input_buffer[3] > 47) && (input_buffer[3] < 58) && (input_buffer_index == 4)) {
        byte pin_value = 0;
        if (toupper(input_buffer[2]) == 'A') {
          pin_value = get_analog_pin(input_buffer[3] - 48);
        } else {
          pin_value = ((input_buffer[2] - 48) * 10) + (input_buffer[3] - 48);
        }
        pinModeEnhanced(pin_value, OUTPUT);
        digitalWriteEnhanced(pin_value, HIGH);
        strcpy(return_string, "OK");
      } else {
        strcpy(return_string, "Error");
      }
      break;
    case 'F':      // \Fxx - turn pin off; xx = pin number
      if ((((input_buffer[2] > 47) && (input_buffer[2] < 58)) || (toupper(input_buffer[2]) == 'A')) && (input_buffer[3] > 47) && (input_buffer[3] < 58) && (input_buffer_index == 4)) {
        byte pin_value = 0;
        if (toupper(input_buffer[2]) == 'A') {
          pin_value = get_analog_pin(input_buffer[3] - 48);
        } else {
          pin_value = ((input_buffer[2] - 48) * 10) + (input_buffer[3] - 48);
        }
        pinModeEnhanced(pin_value, OUTPUT);
        digitalWriteEnhanced(pin_value, LOW);
        strcpy(return_string, "OK");
      } else {
        strcpy(return_string, "Error");
      }
      break;
    case 'W':    // \Wxxyyy - turn on pin PWM; xx = pin number, yyy = PWM value (0-255)
      if (((input_buffer[2] > 47) && (input_buffer[2] < 58)) && (input_buffer[3] > 47) && (input_buffer[3] < 58)  && (input_buffer_index == 7)) {
        byte pin_value = 0;
        if (toupper(input_buffer[2]) == 'A') {
          pin_value = get_analog_pin(input_buffer[3] - 48);
        } else {
          pin_value = ((input_buffer[2] - 48) * 10) + (input_buffer[3] - 48);
        }
        int write_value = ((input_buffer[4] - 48) * 100) + ((input_buffer[5] - 48) * 10) + (input_buffer[6] - 48);
        if ((write_value >= 0) && (write_value < 256)) {
          pinModeEnhanced(pin_value, OUTPUT);
          analogWriteEnhanced(pin_value, write_value);
          strcpy(return_string, "OK");
        } else {
          strcpy(return_string, "Error");
        }
      } else {
        strcpy(return_string, "Error");
      }
      break;
  #endif // FEATURE_ANCILLARY_PIN_CONTROL


    default: strcpy(return_string, "Error.");



  } /* switch */
} /* process_backslash_command */


// --------------------------------------------------------------
#ifdef FEATURE_CLOCK
char * clock_string(){

  char return_string[32] = "";
  char temp_string[16] = "";



  dtostrf(clock_years, 0, 0, temp_string);
  strcpy(return_string, temp_string);
  strcat(return_string, "-");
  if (clock_months < 10) {
    strcat(return_string, "0");
  }
  dtostrf(clock_months, 0, 0, temp_string);
  strcat(return_string, temp_string);
  strcat(return_string, "-");
  if (clock_days < 10) {
    strcat(return_string, "0");
  }
  dtostrf(clock_days, 0, 0, temp_string);
  strcat(return_string, temp_string);
  strcat(return_string, " ");

  if (clock_hours < 10) {
    strcat(return_string, "0");
  }
  dtostrf(clock_hours, 0, 0, temp_string);
  strcat(return_string, temp_string);
  strcat(return_string, ":");
  if (clock_minutes < 10) {
    strcat(return_string, "0");
  }
  dtostrf(clock_minutes, 0, 0, temp_string);
  strcat(return_string, temp_string);
  strcat(return_string, ":");
  if (clock_seconds < 10) {
    strcat(return_string, "0");
  }
  dtostrf(clock_seconds, 0, 0, temp_string);
  strcat(return_string, temp_string);
  strcat(return_string,"Z");
  return return_string;

} /* clock_string */
#endif // FEATURE_CLOCK


// --------------------------------------------------------------
#if defined(FEATURE_MOON_TRACKING) || defined(FEATURE_SUN_TRACKING)
char * az_el_calibrated_string(){

  char return_string[48] = "";
  char tempstring[16] = "";

  read_azimuth(1);
  read_elevation(1);
  strcpy(return_string, "Heading calibrated.  Az: ");
  dtostrf((azimuth / LCD_HEADING_MULTIPLIER), 0, LCD_DECIMAL_PLACES, tempstring);
  strcat(return_string, tempstring);
  #ifdef FEATURE_ELEVATION_CONTROL
  strcat(return_string, " El: ");
  dtostrf((elevation / LCD_HEADING_MULTIPLIER), 0, LCD_DECIMAL_PLACES, tempstring);
  strcat(return_string, tempstring);
  #endif //FEATURE_ELEVATION_CONTROL
  return return_string;

}
#endif // defined(FEATURE_MOON_TRACKING) || defined(FEATURE_SUN_TRACKING)
// --------------------------------------------------------------
void debug_print(char * print_string){

  #if defined(FEATURE_REMOTE_UNIT_SLAVE) || defined(FEATURE_YAESU_EMULATION) || defined(FEATURE_EASYCOM_EMULATION)
  
  if (debug_mode & CONTROL_PORT0){
    control_port->print(print_string);
  }
  #endif //defined(FEATURE_REMOTE_UNIT_SLAVE) || defined(FEATURE_YAESU_EMULATION) || defined(FEATURE_EASYCOM_EMULATION)

  #ifdef FEATURE_ETHERNET
  if (debug_mode & ETHERNET_PORT0){
    ethernetclient0.print(print_string);
  }
  #endif //FEATURE_ETHERNET

  #if defined(FEATURE_ETHERNET) && defined(ETHERNET_TCP_PORT_1)
  if (debug_mode & ETHERNET_PORT1){
    ethernetclient1.print(print_string);
  }
  #endif //defined(FEATURE_ETHERNET) && defined(ETHERNET_TCP_PORT_1)

}
// --------------------------------------------------------------
void debug_println(char * print_string){

  #if defined(FEATURE_REMOTE_UNIT_SLAVE) || defined(FEATURE_YAESU_EMULATION) || defined(FEATURE_EASYCOM_EMULATION)
  if (debug_mode & CONTROL_PORT0){
    control_port->println(print_string);
  }
  #endif //defined(FEATURE_REMOTE_UNIT_SLAVE) || defined(FEATURE_YAESU_EMULATION) || defined(FEATURE_EASYCOM_EMULATION)

  #ifdef FEATURE_ETHERNET
  if (debug_mode & ETHERNET_PORT0){
    ethernetclient0.println(print_string);
  }
  #endif //FEATURE_ETHERNET

  #if defined(FEATURE_ETHERNET) && defined(ETHERNET_TCP_PORT_1)
  if (debug_mode & ETHERNET_PORT1){
    ethernetclient1.println(print_string);
  }
  #endif //defined(FEATURE_ETHERNET) && defined(ETHERNET_TCP_PORT_1)

}
// --------------------------------------------------------------
void debug_print_char(char print_char){

  #if defined(FEATURE_REMOTE_UNIT_SLAVE) || defined(FEATURE_YAESU_EMULATION) || defined(FEATURE_EASYCOM_EMULATION)
  if (debug_mode & CONTROL_PORT0){
    control_port->print(print_char);
  }
  #endif //defined(FEATURE_REMOTE_UNIT_SLAVE) || defined(FEATURE_YAESU_EMULATION) || defined(FEATURE_EASYCOM_EMULATION)

  #ifdef FEATURE_ETHERNET
  if (debug_mode & ETHERNET_PORT0){
    ethernetclient0.print(print_char);
  }
  #endif //FEATURE_ETHERNET

  #if defined(FEATURE_ETHERNET) && defined(ETHERNET_TCP_PORT_1)
  if (debug_mode & ETHERNET_PORT1){
    ethernetclient1.print(print_char);
  }
  #endif //defined(FEATURE_ETHERNET) && defined(ETHERNET_TCP_PORT_1)
}
// --------------------------------------------------------------
void debug_write(char * print_string){

  #if defined(FEATURE_REMOTE_UNIT_SLAVE) || defined(FEATURE_YAESU_EMULATION) || defined(FEATURE_EASYCOM_EMULATION)
  if (debug_mode & CONTROL_PORT0){
    control_port->write(print_string);
  }
  #endif //defined(FEATURE_REMOTE_UNIT_SLAVE) || defined(FEATURE_YAESU_EMULATION) || defined(FEATURE_EASYCOM_EMULATION)

  #ifdef FEATURE_ETHERNET
  if (debug_mode & ETHERNET_PORT0){
    ethernetclient0.write(print_string);
  }
  #endif //FEATURE_ETHERNET

  #if defined(FEATURE_ETHERNET) && defined(ETHERNET_TCP_PORT_1)
  if (debug_mode & ETHERNET_PORT1){
    ethernetclient1.write(print_string);
  }
  #endif //defined(FEATURE_ETHERNET) && defined(ETHERNET_TCP_PORT_1) 

}
// --------------------------------------------------------------
void debug_print_int(int print_int){

  #if defined(FEATURE_REMOTE_UNIT_SLAVE) || defined(FEATURE_YAESU_EMULATION) || defined(FEATURE_EASYCOM_EMULATION)
  if (debug_mode & CONTROL_PORT0){
    control_port->print(print_int);
  }
  #endif //defined(FEATURE_REMOTE_UNIT_SLAVE) || defined(FEATURE_YAESU_EMULATION) || defined(FEATURE_EASYCOM_EMULATION)

  #ifdef FEATURE_ETHERNET
  if (debug_mode & ETHERNET_PORT0){
    ethernetclient0.print(print_int);
  }
  #endif //FEATURE_ETHERNET

  #if defined(FEATURE_ETHERNET) && defined(ETHERNET_TCP_PORT_1)
  if (debug_mode & ETHERNET_PORT1){
    ethernetclient1.print(print_int);
  }
  #endif //defined(FEATURE_ETHERNET) && defined(ETHERNET_TCP_PORT_1) 


}
// --------------------------------------------------------------
void debug_write_int(int write_int){

  #if defined(FEATURE_REMOTE_UNIT_SLAVE) || defined(FEATURE_YAESU_EMULATION) || defined(FEATURE_EASYCOM_EMULATION)
  if (debug_mode & CONTROL_PORT0){
    control_port->write(write_int);
  }
  #endif //defined(FEATURE_REMOTE_UNIT_SLAVE) || defined(FEATURE_YAESU_EMULATION) || defined(FEATURE_EASYCOM_EMULATION)

  #ifdef FEATURE_ETHERNET
  if (debug_mode & ETHERNET_PORT0){
    ethernetclient0.write(write_int);
  }  
  #endif //FEATURE_ETHERNET

  #if defined(FEATURE_ETHERNET) && defined(ETHERNET_TCP_PORT_1)
  if (debug_mode & ETHERNET_PORT1){
    ethernetclient1.write(write_int);
  }  
  #endif //defined(FEATURE_ETHERNET) && defined(ETHERNET_TCP_PORT_1)

}
// --------------------------------------------------------------
void debug_print_float(float print_float,byte places){

  char tempstring[16] = "";

  dtostrf(print_float,0,places,tempstring);

  #if defined(FEATURE_REMOTE_UNIT_SLAVE) || defined(FEATURE_YAESU_EMULATION) || defined(FEATURE_EASYCOM_EMULATION)
  if (debug_mode & CONTROL_PORT0){
    control_port->print(tempstring);
  }
  #endif //defined(FEATURE_REMOTE_UNIT_SLAVE) || defined(FEATURE_YAESU_EMULATION) || defined(FEATURE_EASYCOM_EMULATION)

  #ifdef FEATURE_ETHERNET
  if (debug_mode & ETHERNET_PORT0){
    ethernetclient0.print(tempstring);
  }
  #endif //FEATURE_ETHERNET

  #if defined(FEATURE_ETHERNET) && defined(ETHERNET_TCP_PORT_1)
  if (debug_mode & ETHERNET_PORT1){
    ethernetclient1.print(tempstring);
  }
  #endif //defined(FEATURE_ETHERNET) && defined(ETHERNET_TCP_PORT_1)  

}
// --------------------------------------------------------------
#ifdef FEATURE_MOON_TRACKING
char * moon_status_string(){

    char returnstring[128] = "";
    char tempstring[16] = "";

    strcpy(returnstring,"\tmoon: AZ: ");
    dtostrf(moon_azimuth,0,2,tempstring);
    strcat(returnstring,tempstring);
    strcat(returnstring," EL: ");
    dtostrf(moon_elevation,0,2,tempstring);
    strcat(returnstring,tempstring);
    strcat(returnstring,"\tTRACKING_");
    if (!moon_tracking_active) {
      strcat(returnstring,"IN");
    }
    strcat(returnstring,"ACTIVE ");
    if (moon_tracking_active) {
      if (!moon_visible) {
        strcat(returnstring,"NOT_");
      }
      strcat(returnstring,"VISIBLE");
    }
    return returnstring;
}
#endif // FEATURE_MOON_TRACKING
// --------------------------------------------------------------
#ifdef FEATURE_SUN_TRACKING
char * sun_status_string(){

    char returnstring[128] = "";
    char tempstring[16] = "";

    strcpy(returnstring,"\tsun: AZ: ");
    dtostrf(sun_azimuth,0,2,tempstring);
    strcat(returnstring,tempstring);
    strcat(returnstring," EL: ");
    dtostrf(sun_elevation,0,2,tempstring);
    strcat(returnstring,tempstring);
    strcat(returnstring,"\tTRACKING_");
    if (!sun_tracking_active) {
      strcat(returnstring,"IN");
    }
    strcat(returnstring,"ACTIVE ");
    if (sun_tracking_active) {
      if (!sun_visible) {
        strcat(returnstring,"NOT_");
      }
      strcat(returnstring,"VISIBLE");
    }
    return returnstring;
}
#endif // FEATURE_SUN_TRACKING
// --------------------------------------------------------------
#ifdef FEATURE_CLOCK
char * clock_status_string(){

  switch (clock_status) {
    case FREE_RUNNING: return("FREE_RUNNING"); break;
    case GPS_SYNC: return("GPS_SYNC"); break;
    case RTC_SYNC: return("RTC_SYNC"); break;
    case SLAVE_SYNC: return("SLAVE_SYNC"); break;
    case SLAVE_SYNC_GPS: return("SLAVE_SYNC_GPS"); break;
  }
}
#endif //FEATURE_CLOCK
// --------------------------------------------------------------
#if defined(FEATURE_SUN_TRACKING) || defined(FEATURE_MOON_TRACKING)
char * coordinate_string(){

  char returnstring[32] = "";
  char tempstring[12] = "";

  dtostrf(latitude,0,4,returnstring);
  strcat(returnstring," ");
  dtostrf(longitude,0,4,tempstring);
  strcat(returnstring,tempstring);
  return returnstring;

}
#endif //defined(FEATURE_SUN_TRACKING) || defined(FEATURE_MOON_TRACKING)










// --------------------------------------------------------------
#ifdef FEATURE_YAESU_EMULATION
void process_yaesu_command(byte * yaesu_command_buffer, int yaesu_command_buffer_index, byte source_port, char * return_string){



    char tempstring[11] = "";
    int parsed_value = 0;
  
    int parsed_elevation = 0;
  

    #ifdef FEATURE_TIMED_BUFFER
    int parsed_value2 = 0;
    #endif //FEATURE_TIMED_BUFFER

    strcpy(return_string,"");

    switch (yaesu_command_buffer[0]) {          // look at the first character of the command
      case 'C':                                // C - return current azimuth
        #ifdef DEBUG_PROCESS_YAESU
        if (debug_mode) {
          debug_print("yaesu_serial_command: C\n");
        }
        #endif // DEBUG_PROCESS_YAESU
        #ifdef OPTION_DELAY_C_CMD_OUTPUT
        delay(400);
        #endif    
        //strcpy(return_string,"");
        #ifndef OPTION_GS_232B_EMULATION
        strcat(return_string,"+0");
        #else
        strcat(return_string,"AZ=");
        #endif
        dtostrf(int(azimuth / HEADING_MULTIPLIER),0,0,tempstring);
        if (int(azimuth / HEADING_MULTIPLIER) < 10) {
          strcat(return_string,"0");
        }
        if (int(azimuth / HEADING_MULTIPLIER) < 100) {
          strcat(return_string,"0");
        }
        strcat(return_string,tempstring);
      
        #ifdef FEATURE_ELEVATION_CONTROL
        #ifndef OPTION_C_COMMAND_SENDS_AZ_AND_EL
        if ((yaesu_command_buffer[1] == '2') && (yaesu_command_buffer_index > 1)) {     // did we get the C2 command?
        #endif


        #ifndef OPTION_GS_232B_EMULATION
        if (elevation < 0) {
          strcat(return_string,"-0");
        } else {
          strcat(return_string,"+0");
        }
        #endif
        #ifdef OPTION_GS_232B_EMULATION
        strcat(return_string,"EL=");
        #endif
        dtostrf(int(elevation / HEADING_MULTIPLIER),0,0,tempstring);
        if (int(elevation / HEADING_MULTIPLIER) < 10) {
          strcat(return_string,("0"));
        }
        if (int(elevation / HEADING_MULTIPLIER) < 100) {
          strcat(return_string,"0");
        }
        strcat(return_string,tempstring);

        #ifndef OPTION_C_COMMAND_SENDS_AZ_AND_EL
        } else {
          //strcat(return_string,"\n");
        }
        #endif // OPTION_C_COMMAND_SENDS_AZ_AND_EL
        #endif // FEATURE_ELEVATION_CONTROL
      
        #ifndef FEATURE_ELEVATION_CONTROL
        if ((yaesu_command_buffer[1] == '2') && (yaesu_command_buffer_index > 1)) {     // did we get the C2 command?
          #ifndef OPTION_GS_232B_EMULATION
          strcat(return_string,"+0000");    // return a dummy elevation since we don't have the elevation feature turned on
          #else
          strcat(return_string,"EL=000");
          #endif
        } else {
          //strcat(return_string,"\n");
        }
        #endif // FEATURE_ELEVATION_CONTROL   
        break;
        
        
        //-----------------end of C command-----------------
        
      #ifdef FEATURE_AZ_POSITION_POTENTIOMETER
      case 'F': // F - full scale calibration
        #ifdef DEBUG_PROCESS_YAESU
        if (debug_mode) {
          debug_print("yaesu_serial_command: F\n");
        }
        #endif // DEBUG_PROCESS_YAESU
      
      
        #ifdef FEATURE_ELEVATION_CONTROL
        if ((yaesu_command_buffer[1] == '2') && (yaesu_command_buffer_index > 1)) {     // did we get the F2 command?

          clear_serial_buffer();
          if (source_port == CONTROL_PORT0){
            control_port->println(F("Elevate to 180 (or max elevation) and send keystroke..."));
          }
          get_keystroke();
          read_elevation(1);
          configuration.analog_el_max_elevation = analog_el;
          write_settings_to_eeprom();
          strcpy(return_string,"Wrote to memory");
          return;
        }
        #endif
      
        clear_serial_buffer();
        if (source_port == CONTROL_PORT0){
          control_port->println(F("Rotate to full CW and send keystroke..."));
          get_keystroke();
        }
        read_azimuth(1);
        configuration.analog_az_full_cw = analog_az;
        write_settings_to_eeprom();
        strcpy(return_string,"Wrote to memory");     
        break;
        #endif // FEATURE_AZ_POSITION_POTENTIOMETER
      case 'H': print_help(source_port); break;                     // H - print help - depricated
      case 'L':  // L - manual left (CCW) rotation
        #ifdef DEBUG_PROCESS_YAESU
        if (debug_mode) {
          debug_print("yaesu_serial_command: L\n");
        }
        #endif // DEBUG_PROCESS_YAESU
        submit_request(AZ, REQUEST_CCW, 0, 21);
        //strcpy(return_string,"\n");
        #ifdef FEATURE_PARK
        deactivate_park();
        #endif // FEATURE_PARK
        break;
        
      #ifdef FEATURE_AZ_POSITION_POTENTIOMETER
      case 'O':  // O - offset calibration
        #ifdef DEBUG_PROCESS_YAESU
        if (debug_mode) {
          debug_print("yaesu_serial_command: O\n");
        }
        #endif // DEBUG_PROCESS_YAESU

        #ifdef FEATURE_ELEVATION_CONTROL
        if ((yaesu_command_buffer[1] == '2') && (yaesu_command_buffer_index > 1)) {     // did we get the O2 command?       
          clear_serial_buffer();
          if (source_port == CONTROL_PORT0){  
            control_port->println(F("Elevate to 0 degrees and send keystroke..."));
          }
          get_keystroke();
          read_elevation(1);
          configuration.analog_el_0_degrees = analog_el;
          write_settings_to_eeprom();
          strcpy(return_string,"Wrote to memory");
          return;
        }
        #endif
      
        clear_serial_buffer();  
        if (source_port == CONTROL_PORT0){    
          control_port->println(F("Rotate to full CCW and send keystroke..."));
        }
        get_keystroke();
        read_azimuth(1);
        configuration.analog_az_full_ccw = analog_az;
        write_settings_to_eeprom();
        strcpy(return_string,"Wrote to memory");
        break;
        #endif // FEATURE_AZ_POSITION_POTENTIOMETER
      
      case 'R':  // R - manual right (CW) rotation
        #ifdef DEBUG_PROCESS_YAESU
        if (debug_mode) {
          debug_print("yaesu_serial_command: R\n");
        }
        #endif // DEBUG_PROCESS_YAESU
        submit_request(AZ, REQUEST_CW, 0, 22);
        strcpy(return_string,"\n");
        #ifdef FEATURE_PARK
        deactivate_park();
        #endif // FEATURE_PARK
        break;
        
      case 'A':  // A - CW/CCW rotation stop
        #ifdef DEBUG_PROCESS_YAESU
        if (debug_mode) {
          debug_print("yaesu_serial_command: A\n");
        }
        #endif // DEBUG_PROCESS_YAESU
        submit_request(AZ, REQUEST_STOP, 0, 23);
        //strcpy(return_string,"\n");
        #ifdef FEATURE_PARK
        deactivate_park();
        #endif // FEATURE_PARK
        break;
        
      case 'S':         // S - all stop
        #ifdef DEBUG_PROCESS_YAESU
        if (debug_mode) {
          debug_print("yaesu_serial_command: S\n");
        }
        #endif // DEBUG_PROCESS_YAESU
        submit_request(AZ, REQUEST_STOP, 0, 24);
        #ifdef FEATURE_ELEVATION_CONTROL
        submit_request(EL, REQUEST_STOP, 0, 25);
        #endif
        #ifdef FEATURE_TIMED_BUFFER
        clear_timed_buffer();
        #endif // FEATURE_TIMED_BUFFER
        //strcpy(return_string,"");
        #ifdef FEATURE_PARK
        deactivate_park();
        #endif // FEATURE_PARK
        break;
        
      case 'M': // M - auto azimuth rotation
        #ifdef DEBUG_PROCESS_YAESU
        if (debug_mode) {
          debug_print("yaesu_serial_command: M\n");
        }
        #endif // DEBUG_PROCESS_YAESU
        #ifdef FEATURE_PARK
        deactivate_park();
        #endif // FEATURE_PARK
  
        if (yaesu_command_buffer_index > 4) {  // if there are more than 4 characters in the command buffer, we got a timed interval command
          #ifdef FEATURE_TIMED_BUFFER
          clear_timed_buffer();
          parsed_value = ((int(yaesu_command_buffer[1]) - 48) * 100) + ((int(yaesu_command_buffer[2]) - 48) * 10) + (int(yaesu_command_buffer[3]) - 48);
          if ((parsed_value > 0) && (parsed_value < 1000)) {
            timed_buffer_interval_value_seconds = parsed_value;
            for (int x = 5; x < yaesu_command_buffer_index; x = x + 4) {
              parsed_value = ((int(yaesu_command_buffer[x]) - 48) * 100) + ((int(yaesu_command_buffer[x + 1]) - 48) * 10) + (int(yaesu_command_buffer[x + 2]) - 48);
              if ((parsed_value >= 0) && (parsed_value <= 360)) {  // is it a valid azimuth?
                timed_buffer_azimuths[timed_buffer_number_entries_loaded] = parsed_value * HEADING_MULTIPLIER;
                timed_buffer_number_entries_loaded++;
                timed_buffer_status = LOADED_AZIMUTHS;
                if (timed_buffer_number_entries_loaded > TIMED_INTERVAL_ARRAY_SIZE) {   // is the array full?
                  submit_request(AZ, REQUEST_AZIMUTH, timed_buffer_azimuths[0], 26);  // array is full, go to the first azimuth
                  timed_buffer_entry_pointer = 1;
                  return;
                }
              } else {   // we hit an invalid bearing
                timed_buffer_status = EMPTY;
                timed_buffer_number_entries_loaded = 0;
                strcpy(return_string,"?>");  // error
                return;
              }
            }
            submit_request(AZ, REQUEST_AZIMUTH, timed_buffer_azimuths[0], 27);   // go to the first azimuth
            timed_buffer_entry_pointer = 1;       
          } else {
            strcpy(return_string,"?>");  // error
          }
          #else
          strcpy(return_string,"?>");
          #endif // FEATURE_TIMED_BUFFER
          return;
        } else {                         // if there are four characters, this is just a single direction setting
          if (yaesu_command_buffer_index == 4) {
            parsed_value = ((int(yaesu_command_buffer[1]) - 48) * 100) + ((int(yaesu_command_buffer[2]) - 48) * 10) + (int(yaesu_command_buffer[3]) - 48);
            #ifdef FEATURE_TIMED_BUFFER
            clear_timed_buffer();
            #endif // FEATURE_TIMED_BUFFER
            if ((parsed_value >= 0) && (parsed_value <= (azimuth_starting_point + azimuth_rotation_capability))) {
              submit_request(AZ, REQUEST_AZIMUTH, (parsed_value * HEADING_MULTIPLIER), 28);
              return;
            }
          }
        }
        strcpy(return_string,"?>");      
        break;
        
      #ifdef FEATURE_TIMED_BUFFER
      case 'N': // N - number of loaded timed interval entries
        #ifdef DEBUG_PROCESS_YAESU
        if (debug_mode) {
          debug_print("yaesu_serial_command: N\n");
        }
        #endif // DEBUG_PROCESS_YAESU
        #ifdef FEATURE_PARK
        deactivate_park();
        #endif // FEATURE_PARK
        sprintf(return_string,"%d",timed_buffer_number_entries_loaded);
        break;
        #endif // FEATURE_TIMED_BUFFER
        
      #ifdef FEATURE_TIMED_BUFFER
      case 'T': // T - initiate timed tracking
        initiate_timed_buffer(source_port);
        #ifdef FEATURE_PARK
        deactivate_park();
        #endif // FEATURE_PARK
        break;           
        #endif // FEATURE_TIMED_BUFFER
        
      case 'X':  // X - azimuth speed change
        #ifdef DEBUG_PROCESS_YAESU
        if (debug_mode) {
          debug_print("yaesu_serial_command: X\n");
        }
        #endif // DEBUG_PROCESS_YAESU
        
        
        if (yaesu_command_buffer_index > 1) {
          switch (yaesu_command_buffer[1]) {
            case '4':
              normal_az_speed_voltage = PWM_SPEED_VOLTAGE_X4;
              update_az_variable_outputs(PWM_SPEED_VOLTAGE_X4);
              #if defined(FEATURE_ELEVATION_CONTROL) && defined(OPTION_EL_SPEED_FOLLOWS_AZ_SPEED)
              normal_el_speed_voltage = PWM_SPEED_VOLTAGE_X4;
              update_el_variable_outputs(PWM_SPEED_VOLTAGE_X4);
              #endif
              strcpy(return_string,"Speed X4");
              break;
            case '3':
              normal_az_speed_voltage = PWM_SPEED_VOLTAGE_X3;
              update_az_variable_outputs(PWM_SPEED_VOLTAGE_X3);
              #if defined(FEATURE_ELEVATION_CONTROL) && defined(OPTION_EL_SPEED_FOLLOWS_AZ_SPEED)
              normal_el_speed_voltage = PWM_SPEED_VOLTAGE_X3;
              update_el_variable_outputs(PWM_SPEED_VOLTAGE_X3);
              #endif
              strcpy(return_string,"Speed X3");
              break;
            case '2':
              normal_az_speed_voltage = PWM_SPEED_VOLTAGE_X2;
              update_az_variable_outputs(PWM_SPEED_VOLTAGE_X2);
              #if defined(FEATURE_ELEVATION_CONTROL) && defined(OPTION_EL_SPEED_FOLLOWS_AZ_SPEED)
              normal_el_speed_voltage = PWM_SPEED_VOLTAGE_X2;
              update_el_variable_outputs(PWM_SPEED_VOLTAGE_X2);
              #endif
              strcpy(return_string,"Speed X2");
              break;
            case '1':
              normal_az_speed_voltage = PWM_SPEED_VOLTAGE_X1;
              update_az_variable_outputs(PWM_SPEED_VOLTAGE_X1);
              #if defined(FEATURE_ELEVATION_CONTROL) && defined(OPTION_EL_SPEED_FOLLOWS_AZ_SPEED)
              normal_el_speed_voltage = PWM_SPEED_VOLTAGE_X1;
              update_el_variable_outputs(PWM_SPEED_VOLTAGE_X1);
              #endif
              strcpy(return_string,"Speed X1");
              break;
            default: strcpy(return_string,"?>"); break;
          } /* switch */
        } else {
          strcpy(return_string,"?>");
        }
        break;
        
      #ifdef FEATURE_ELEVATION_CONTROL
      case 'U':  // U - manual up rotation
        #ifdef DEBUG_PROCESS_YAESU
        if (debug_mode) {
          debug_print("yaesu_serial_command: U\n");
        }
        #endif // DEBUG_PROCESS_YAESU
        #ifdef FEATURE_PARK
        deactivate_park();
        #endif // FEATURE_PARK
        submit_request(EL, REQUEST_UP, 0, 29);
        //strcpy(return_string,"\n");
        break;
        
      case 'D':  // D - manual down rotation
        #ifdef DEBUG_PROCESS_YAESU
        if (debug_mode) {
          debug_print("yaesu_serial_command: D\n");
        }
        #endif // DEBUG_PROCESS_YAESU
        #ifdef FEATURE_PARK
        deactivate_park();
        #endif // FEATURE_PARK
        submit_request(EL, REQUEST_DOWN, 0, 30);
        //strcpy(return_string,"\n");
        break;
        
      case 'E':  // E - stop elevation rotation
        #ifdef DEBUG_PROCESS_YAESU
        if (debug_mode) {
          debug_print("yaesu_serial_command: E\n");
        }
        #endif // DEBUG_PROCESS_YAESU
        #ifdef FEATURE_PARK
        deactivate_park();
        #endif // FEATURE_PARK
        submit_request(EL, REQUEST_STOP, 0, 31);
        //strcpy(return_string,"\n");
        break;
        
      case 'B': // B - return current elevation
        #ifndef OPTION_GS_232B_EMULATION
        if (elevation < 0) {
          strcat(return_string,"-0");
        } else {
          strcat(return_string,"+0");
        }
        #else
        strcat(return_string,"EL=");
        #endif //OPTION_GS_232B_EMULATION
        dtostrf(int(elevation / HEADING_MULTIPLIER),0,0,tempstring);
        if (int(elevation / HEADING_MULTIPLIER) < 10) {
          strcat(return_string,("0"));
        }
        if (int(elevation / HEADING_MULTIPLIER) < 100) {
          strcat(return_string,"0");
        }
        strcat(return_string,tempstring);
      break;        
      
      #endif /* ifdef FEATURE_ELEVATION_CONTROL */
      
      case 'W':  // W - auto elevation rotation
        #ifdef DEBUG_PROCESS_YAESU
        if (debug_mode) {
          debug_print("yaesu_serial_command: W\n");
        }
        #endif // DEBUG_PROCESS_YAESU
        #ifdef FEATURE_PARK
        deactivate_park();
        #endif // FEATURE_PARK
        
        
        // parse out W command
        // Short Format: WXXX YYYY           XXX = azimuth YYY = elevation
        // Long Format : WSSS XXX YYY        SSS = timed interval   XXX = azimuth    YYY = elevation

        if (yaesu_command_buffer_index > 8) {  // if there are more than 4 characters in the command buffer, we got a timed interval command
          #if defined(FEATURE_TIMED_BUFFER) && defined(FEATURE_ELEVATION_CONTROL) 
          parsed_value = ((int(yaesu_command_buffer[1]) - 48) * 100) + ((int(yaesu_command_buffer[2]) - 48) * 10) + (int(yaesu_command_buffer[3]) - 48);
          if ((parsed_value > 0) && (parsed_value < 1000)) {
            timed_buffer_interval_value_seconds = parsed_value;
            for (int x = 5; x < yaesu_command_buffer_index; x = x + 8) {
              parsed_value = ((int(yaesu_command_buffer[x]) - 48) * 100) + ((int(yaesu_command_buffer[x + 1]) - 48) * 10) + (int(yaesu_command_buffer[x + 2]) - 48);
              parsed_value2 = ((int(yaesu_command_buffer[x + 4]) - 48) * 100) + ((int(yaesu_command_buffer[x + 5]) - 48) * 10) + (int(yaesu_command_buffer[x + 6]) - 48);
              if ((parsed_value > -1) && (parsed_value < 361) && (parsed_value2 > -1) && (parsed_value2 < 181)) {  // is it a valid azimuth?
                timed_buffer_azimuths[timed_buffer_number_entries_loaded] = (parsed_value * HEADING_MULTIPLIER);
                timed_buffer_elevations[timed_buffer_number_entries_loaded] = (parsed_value2 * HEADING_MULTIPLIER);
                timed_buffer_number_entries_loaded++;
                timed_buffer_status = LOADED_AZIMUTHS_ELEVATIONS;
                if (timed_buffer_number_entries_loaded > TIMED_INTERVAL_ARRAY_SIZE) {   // is the array full?
                  x = yaesu_command_buffer_index;  // array is full, go to the first azimuth and elevation
      
                }
              } else {   // we hit an invalid bearing
                timed_buffer_status = EMPTY;
                timed_buffer_number_entries_loaded = 0;
                strcpy(return_string,"?>");  // error
                return;
              }
            }
          }
          timed_buffer_entry_pointer = 1;             // go to the first bearings
          parsed_value = timed_buffer_azimuths[0];
          parsed_elevation = timed_buffer_elevations[0];
          #else /* ifdef FEATURE_TIMED_BUFFER FEATURE_ELEVATION_CONTROL*/
          strcpy(return_string,"?>");
          #endif // FEATURE_TIMED_BUFFER FEATURE_ELEVATION_CONTROL
        } else {
          // this is a short form W command, just parse the azimuth and elevation and initiate rotation
          parsed_value = (((int(yaesu_command_buffer[1]) - 48) * 100) + ((int(yaesu_command_buffer[2]) - 48) * 10) + (int(yaesu_command_buffer[3]) - 48)) * HEADING_MULTIPLIER;
          parsed_elevation = (((int(yaesu_command_buffer[5]) - 48) * 100) + ((int(yaesu_command_buffer[6]) - 48) * 10) + (int(yaesu_command_buffer[7]) - 48)) * HEADING_MULTIPLIER;
        }
      
         #ifndef FEATURE_ELEVATION_CONTROL
        if ((parsed_value >= 0) && (parsed_value <= (360 * HEADING_MULTIPLIER))) {
          submit_request(AZ, REQUEST_AZIMUTH, parsed_value, 32);
        } else {
          #ifdef DEBUG_PROCESS_YAESU
          if (debug_mode) {
            debug_print("process_yaesu_command: W cmd az error");
          }
          #endif // DEBUG_PROCESS_YAESU
          strcpy(return_string,"?>");      // bogus elevation - return and error and don't do anything
        }
        
        #else
         if ((parsed_value >= 0) && (parsed_value <= (360 * HEADING_MULTIPLIER)) && (parsed_elevation >= 0) && (parsed_elevation <= (180 * HEADING_MULTIPLIER))) {
          submit_request(AZ, REQUEST_AZIMUTH, parsed_value, 33);
          submit_request(EL, REQUEST_ELEVATION, parsed_elevation, 34);
        } else {
          #ifdef DEBUG_PROCESS_YAESU
          if (debug_mode) {
            debug_print("process_yaesu_command: W cmd az/el error");
          }
          #endif // DEBUG_PROCESS_YAESU
          strcpy(return_string,"?>");      // bogus elevation - return and error and don't do anything
        } 
        #endif // FEATURE_ELEVATION_CONTROL
        
        
        break;
        
      #ifdef OPTION_GS_232B_EMULATION
      case 'P':  // P - switch between 360 and 450 degree mode

        if ((yaesu_command_buffer[1] == '3') && (yaesu_command_buffer_index > 2)) {  // P36 command
          azimuth_rotation_capability = 360;
          strcpy(return_string,"Mode 360 degree");
          // write_settings_to_eeprom();
        } else {
          if ((yaesu_command_buffer[1] == '4') && (yaesu_command_buffer_index > 2)) { // P45 command
            azimuth_rotation_capability = 450;
            strcpy(return_string,"Mode 450 degree");
            // write_settings_to_eeprom();
          } else {
            strcpy(return_string,"?>");
          }
        }
   
      
      break;                 
      case 'Z':                                           // Z - Starting point toggle
        if (azimuth_starting_point == 180) {
          azimuth_starting_point = 0;
          strcpy(return_string,"N");
        } else {
          azimuth_starting_point = 180;
          strcpy(return_string,"S");
        }
        strcat(return_string," Center");
        // write_settings_to_eeprom();
        break;
        #endif
        
      default:
        strcpy(return_string,"?>");
        #ifdef DEBUG_PROCESS_YAESU
        if (debug_mode) {
          debug_print("process_yaesu_command: yaesu_command_buffer_index: ");
          debug_print_int(yaesu_command_buffer_index);
          for (int debug_x = 0; debug_x < yaesu_command_buffer_index; debug_x++) {
            debug_print("process_yaesu_command: yaesu_command_buffer[");
            debug_print_int(debug_x);
            debug_print("]: ");
            debug_print_int(yaesu_command_buffer[debug_x]);
            debug_print(" ");
            debug_write_int(yaesu_command_buffer[debug_x]);
            debug_print("\n");;
          }
        }
        #endif // DEBUG_PROCESS_YAESU
    } /* switch */

} /* yaesu_serial_command */
  #endif // FEATURE_YAESU_EMULATION
// --------------------------------------------------------------
#ifdef FEATURE_ETHERNET
void service_ethernet(){


  byte incoming_byte = 0;
  static unsigned long last_incoming_byte_receive_time = 0;
  char return_string[100] = ""; 
  static byte ethernet_port_buffer0[COMMAND_BUFFER_SIZE];
  static int ethernet_port_buffer_index0 = 0;
  static byte first_connect_occurred = 0;
  static long last_received_byte0 = 0;

  #ifdef FEATURE_REMOTE_UNIT_SLAVE
  static byte preamble_received = 0;
  #endif //FEATURE_REMOTE_UNIT_SLAVE

  /*  this is the server side (receiving bytes from a client such as a master unit receiving commands from a computer
      or a slave receiving commands from a master unit

  */

 
  // clear things out if we received a partial message and it's been awhile
  if ((ethernet_port_buffer_index0) && ((millis()-last_received_byte0) > ETHERNET_MESSAGE_TIMEOUT_MS)){
    ethernet_port_buffer_index0 = 0;
    #ifdef FEATURE_REMOTE_UNIT_SLAVE
    preamble_received = 0;
    #endif //FEATURE_REMOTE_UNIT_SLAVE
  }


  if (ethernetserver0.available()){
    ethernetclient0 = ethernetserver0.available();

    last_received_byte0 = millis();

    if (!first_connect_occurred){  // clean out the cruft that's alway spit out on first connect
      while(ethernetclient0.available()){ethernetclient0.read();}
      first_connect_occurred = 1;
      return;
    }    

    if (ethernetclient0.available() > 0){        // the client has sent something
      incoming_byte = ethernetclient0.read();
      last_incoming_byte_receive_time = millis();

      #ifdef DEBUG_ETHERNET
      debug_print("service_ethernet: client:") ;
      debug_print(" char:");
      debug_print_char((char) incoming_byte);
      debug_print("\n");
      #endif //DEBUG_ETHERNET  

      if ((incoming_byte > 96) && (incoming_byte < 123)) {  // uppercase it
        incoming_byte = incoming_byte - 32;
      }          

      char ethernet_preamble[] = ETHERNET_PREAMBLE;

      #ifdef FEATURE_REMOTE_UNIT_SLAVE
      if (preamble_received < 254){         // the master/slave ethernet link has each message prefixed with a preamble
        if (ethernet_preamble[preamble_received] == 0){
          preamble_received = 254;
        } else {
          if (incoming_byte == ethernet_preamble[preamble_received]){
            preamble_received++;
          } else {
            preamble_received = 0;
          }
        }
      }
      // add it to the buffer if it's not a line feed or carriage return and we've received the preamble
      if ((incoming_byte != 10) && (incoming_byte != 13) && (preamble_received == 254)) { 
        ethernet_port_buffer0[ethernet_port_buffer_index0] = incoming_byte;
        ethernet_port_buffer_index0++;
      }
      #else 
      if ((incoming_byte != 10) && (incoming_byte != 13)) { // add it to the buffer if it's not a line feed or carriage return
        ethernet_port_buffer0[ethernet_port_buffer_index0] = incoming_byte;
        ethernet_port_buffer_index0++;
      }
      #endif //FEATURE_REMOTE_UNIT_SLAVE


      if (((incoming_byte == 13) || (ethernet_port_buffer_index0 >= COMMAND_BUFFER_SIZE)) && (ethernet_port_buffer_index0 > 0)){  // do we have a carriage return?
        if ((ethernet_port_buffer0[0] == '\\') or (ethernet_port_buffer0[0] == '/')) {
          process_backslash_command(ethernet_port_buffer0, ethernet_port_buffer_index0, ETHERNET_PORT0, return_string);
        } else {
          #ifdef FEATURE_YAESU_EMULATION
          process_yaesu_command(ethernet_port_buffer0,ethernet_port_buffer_index0,ETHERNET_PORT0,return_string);
          #endif //FEATURE_YAESU_EMULATION
          #ifdef FEATURE_EASYCOM_EMULATION
          process_easycom_command(ethernet_port_buffer0,ethernet_port_buffer_index0,ETHERNET_PORT0,return_string);
          #endif //FEATURE_EASYCOM_EMULATION
          #ifdef FEATURE_REMOTE_UNIT_SLAVE
          process_remote_slave_command(ethernet_port_buffer0,ethernet_port_buffer_index0,ETHERNET_PORT0,return_string);
          #endif //FEATURE_REMOTE_UNIT_SLAVE          
        }  
        ethernetclient0.println(return_string);
        ethernet_port_buffer_index0 = 0;
        #ifdef FEATURE_REMOTE_UNIT_SLAVE
        preamble_received = 0;
        #endif //FEATURE_REMOTE_UNIT_SLAVE
      }

    }
  }


  #ifdef ETHERNET_TCP_PORT_1
  static byte ethernet_port_buffer1[COMMAND_BUFFER_SIZE];
  static int ethernet_port_buffer_index1 = 0;

  if (ethernetserver1.available()){

    ethernetclient1 = ethernetserver1.available();

    if (ethernetclient1.available() > 0){        // the client has sent something
      incoming_byte = ethernetclient1.read();
      last_incoming_byte_receive_time = millis();

      #ifdef DEBUG_ETHERNET
      debug_print("service_ethernet: client:") ;
      debug_print(" char:");
      debug_print_char((char) incoming_byte);
      debug_print("\n");
      #endif //DEBUG_ETHERNET  

      if ((incoming_byte > 96) && (incoming_byte < 123)) {  // uppercase it
        incoming_byte = incoming_byte - 32;
      }                                                                                                                    
      if ((incoming_byte != 10) && (incoming_byte != 13)) { // add it to the buffer if it's not a line feed or carriage return
        ethernet_port_buffer1[ethernet_port_buffer_index1] = incoming_byte;
        ethernet_port_buffer_index1++;
      }
      if (incoming_byte == 13) {  // do we have a carriage return?
        if ((ethernet_port_buffer1[0] == '\\') or (ethernet_port_buffer1[0] == '/')) {
          process_backslash_command(ethernet_port_buffer1, ethernet_port_buffer_index1, ETHERNET_PORT1, return_string);
        } else {
          #ifdef FEATURE_YAESU_EMULATION
          process_yaesu_command(ethernet_port_buffer1,ethernet_port_buffer_index1,ETHERNET_PORT1,return_string);
          #endif //FEATURE_YAESU_EMULATION
          #ifdef FEATURE_EASYCOM_EMULATION
          process_easycom_command(ethernet_port_buffer1,ethernet_port_buffer_index1,ETHERNET_PORT1,return_string);
          #endif //FEATURE_EASYCOM_EMULATION
          #ifdef FEATURE_REMOTE_UNIT_SLAVE
          process_remote_slave_command(ethernet_port_buffer1,ethernet_port_buffer_index1,ETHERNET_PORT1,return_string);
          #endif //FEATURE_REMOTE_UNIT_SLAVE
        }  
        ethernetclient1.println(return_string);
        ethernet_port_buffer_index1 = 0;
      }

    }
  }
  #endif //ETHERNET_TCP_PORT_1

  #ifdef FEATURE_MASTER_WITH_ETHERNET_SLAVE
  static long last_connect_try = 0;
  static long last_received_byte_time = 0;
  byte incoming_ethernet_byte = 0;
  static byte first_ethernet_slave_connect_occurred = 0;

  // are we disconnected and is it time to reconnect?
  if ((ethernetslavelinkclient0_state == ETHERNET_SLAVE_DISCONNECTED)  && (((millis()-last_connect_try) >= ETHERNET_SLAVE_RECONNECT_TIME_MS) || (last_connect_try == 0))){

    #ifdef DEBUG_ETHERNET
    debug_println("service_ethernet: master_slave_ethernet: connecting");
    #endif //DEBUG_ETHERNET

    if (ethernetslavelinkclient0.connect(slave_unit_ip, ETHERNET_SLAVE_TCP_PORT)){
      ethernetslavelinkclient0_state = ETHERNET_SLAVE_CONNECTED;
      if (!first_ethernet_slave_connect_occurred){
        first_ethernet_slave_connect_occurred = 1;
        ethernet_slave_reconnects = 65535;
      }
    } else {
      ethernetslavelinkclient0.stop();
      #ifdef DEBUG_ETHERNET
      debug_println("service_ethernet: master_slave_ethernet: connect failed");
      #endif //DEBUG_ETHERNET
    }

    ethernet_slave_reconnects++;
    last_connect_try = millis();
  }


  if (ethernetslavelinkclient0.available()) {
    incoming_ethernet_byte = ethernetslavelinkclient0.read();

    #ifdef DEBUG_ETHERNET
    debug_print("service_ethernet: slave rx: ");
    debug_print_char(incoming_ethernet_byte);
    debug_print(" : ");
    debug_print_int(incoming_ethernet_byte);
    debug_println("");
    #endif //DEBUG_ETHERNET      

    if (remote_port_rx_sniff) {
      control_port->write(incoming_ethernet_byte);
    }

    if ((incoming_ethernet_byte != 10) && (remote_unit_port_buffer_index < COMMAND_BUFFER_SIZE)) {
      remote_unit_port_buffer[remote_unit_port_buffer_index] = incoming_ethernet_byte;
      remote_unit_port_buffer_index++;
      if ((incoming_ethernet_byte == 13) || (remote_unit_port_buffer_index >= COMMAND_BUFFER_SIZE)) {
        remote_unit_port_buffer_carriage_return_flag = 1;
        #ifdef DEBUG_ETHERNET
        debug_println("service_ethernet: remote_unit_port_buffer_carriage_return_flag");
        #endif //DEBUG_ETHERNET          
      }
    }
    last_received_byte_time = millis();

  }

  if (((millis() - last_received_byte_time) >= ETHERNET_MESSAGE_TIMEOUT_MS) && (remote_unit_port_buffer_index > 1) && (!remote_unit_port_buffer_carriage_return_flag)){
    remote_unit_port_buffer_index = 0;
    #ifdef DEBUG_ETHERNET
    debug_println("service_ethernet: master_slave_ethernet: remote_unit_incoming_buffer_timeout");
    #endif //DEBUG_ETHERNET    
    remote_unit_incoming_buffer_timeouts++;
  }

  if ((ethernetslavelinkclient0_state == ETHERNET_SLAVE_CONNECTED) && (!ethernetslavelinkclient0.connected())){
    ethernetslavelinkclient0.stop();
    ethernetslavelinkclient0_state = ETHERNET_SLAVE_DISCONNECTED;
    remote_unit_port_buffer_index = 0;
    #ifdef DEBUG_ETHERNET
    debug_println("service_ethernet: master_slave_ethernet: lost connection");
    #endif //DEBUG_ETHERNET    
  }


  #endif //FEATURE_MASTER_WITH_ETHERNET_SLAVE

}
#endif //FEATURE_ETHERNET
// --------------------------------------------------------------
#ifdef FEATURE_EASYCOM_EMULATION
void process_easycom_command(byte * easycom_command_buffer, int easycom_command_buffer_index, byte source_port, char * return_string){


  /* Easycom protocol implementation
   *
   * Implemented commands:
   *
   * Command      Meaning     Parameters
   * -------      -------     ----------
   *
   * ML           Move Left
   * MR           Move Right
   * MU           Move Up
   * MD           Move Down
   * SA           Stop azimuth moving
   * SE           Stop elevation moving
   *
   * VE           Request Version
   * AZ           Azimuth     number - 1 decimal place (activated with OPTION_EASYCOM_AZ_QUERY_COMMAND)
   * EL           Elevation   number - 1 decimal place (activated with OPTION_EASYCOM_EL_QUERY_COMMAND)
   *
   *
   */



  char tempstring[11] = "";
  float heading = -1;
  strcpy(return_string,"");

  switch (easycom_command_buffer[0]) { // look at the first character of the command
    #if defined(OPTION_HAMLIB_EASYCOM_AZ_EL_COMMAND_HACK) && defined(FEATURE_ELEVATION_CONTROL)  
    case 'Z':
      //strcpy(return_string,"+");
      strcpy(return_string,"AZ");
      dtostrf((float)azimuth/(float)HEADING_MULTIPLIER,0,1,tempstring);
      strcat(return_string,tempstring);
      //if (elevation >= 0){
        //strcat(return_string,"+");
        strcat(return_string," EL");
      //}
      dtostrf((float)elevation/(float)HEADING_MULTIPLIER,0,1,tempstring);      
      strcat(return_string,tempstring);
      break;
    #endif //OPTION_HAMLIB_EASYCOM_AZ_EL_COMMAND_HACK
    case 'A':  // AZ
      if (easycom_command_buffer[1] == 'Z') {  // format is AZx.x or AZxx.x or AZxxx.x (why didn't they make it fixed length?)
        switch (easycom_command_buffer_index) {
          #ifdef OPTION_EASYCOM_AZ_QUERY_COMMAND
          case 2:
            //strcpy(return_string,"AZ");
            strcpy(return_string,"+");
            dtostrf((float)azimuth/(float)HEADING_MULTIPLIER,0,1,tempstring);
            strcat(return_string,tempstring);
            return;
            break;
          #endif // OPTION_EASYCOM_AZ_QUERY_COMMAND
          case 5: // format AZx.x
            heading = (easycom_command_buffer[2] - 48) + ((easycom_command_buffer[4] - 48) / 10.);
            break;
          case 6: // format AZxx.x
            heading = ((easycom_command_buffer[2] - 48) * 10.) + (easycom_command_buffer[3] - 48) + ((easycom_command_buffer[5] - 48) / 10.);
            break;
          case 7: // format AZxxx.x
            heading = ((easycom_command_buffer[2] - 48) * 100.) + ((easycom_command_buffer[3] - 48) * 10.) + (easycom_command_buffer[4] - 48.) + ((easycom_command_buffer[6] - 48) / 10.);
            break;
            // default: control_port->println("?"); break;
        }
        if (((heading >= 0) && (heading < 451))  && (easycom_command_buffer[easycom_command_buffer_index - 2] == '.')) {
          submit_request(AZ, REQUEST_AZIMUTH, (heading * HEADING_MULTIPLIER), 36);
        } else {
          strcpy(return_string,"?");
        }
      } else {
        strcpy(return_string,"?");
      }
      break;
      #ifdef FEATURE_ELEVATION_CONTROL
    case 'E':  // EL
      if (easycom_command_buffer[1] == 'L') {
        switch (easycom_command_buffer_index) {
          #ifdef OPTION_EASYCOM_EL_QUERY_COMMAND
          case 2:
            //strcpy(return_string,"EL");
            if (elevation >= 0){
              strcpy(return_string,"+");
            }
            dtostrf((float)elevation/(float)HEADING_MULTIPLIER,0,1,tempstring);
            strcat(return_string,tempstring);            
            return;
            break;
          #endif // OPTION_EASYCOM_EL_QUERY_COMMAND
          case 5: // format ELx.x
            heading = (easycom_command_buffer[2] - 48) + ((easycom_command_buffer[4] - 48) / 10.);
            break;
          case 6: // format ELxx.x
            heading = ((easycom_command_buffer[2] - 48) * 10.) + (easycom_command_buffer[3] - 48) + ((easycom_command_buffer[5] - 48) / 10.);
            break;
          case 7: // format ELxxx.x
            heading = ((easycom_command_buffer[2] - 48) * 100.) + ((easycom_command_buffer[3] - 48) * 10.) + (easycom_command_buffer[4] - 48) + ((easycom_command_buffer[6] - 48) / 10.);
            break;
            // default: control_port->println("?"); break;
        }
        if (((heading >= 0) && (heading < 181)) && (easycom_command_buffer[easycom_command_buffer_index - 2] == '.')) {
          submit_request(EL, REQUEST_ELEVATION, (heading * HEADING_MULTIPLIER), 37);
        } else {
          strcpy(return_string,"?");
        }
      } else {
        strcpy(return_string,"?");
      }
      break;
      #endif // #FEATURE_ELEVATION_CONTROL
    case 'S':  // SA or SE - stop azimuth, stop elevation
      switch (easycom_command_buffer[1]) {
        case 'A':
          submit_request(AZ, REQUEST_STOP, 0, 38);
          break;
        #ifdef FEATURE_ELEVATION_CONTROL
        case 'E':
          submit_request(EL, REQUEST_STOP, 0, 39);
          break;
        #endif // FEATURE_ELEVATION_CONTROL
        default: strcpy(return_string,"?"); break;
      }
      break;
    case 'M':  // ML, MR, MU, MD - move left, right, up, down
      switch (easycom_command_buffer[1]) {
        case 'L': // ML - move left
          submit_request(AZ, REQUEST_CCW, 0, 40);
          break;
        case 'R': // MR - move right
          submit_request(AZ, REQUEST_CW, 0, 41);
          break;
        #ifdef FEATURE_ELEVATION_CONTROL
        case 'U': // MU - move up
          submit_request(EL, REQUEST_UP, 0, 42);
          break;
        case 'D': // MD - move down
          submit_request(EL, REQUEST_DOWN, 0, 43);
          break;
        #endif // FEATURE_ELEVATION_CONTROL
        default: strcpy(return_string,"?"); break;
      }
      break;
    case 'V': // VE - version query
      if (easycom_command_buffer[1] == 'E') {
        strcpy(return_string,"VE002");
      }                                                                       // not sure what to send back, sending 002 because this is easycom version 2?
      break;
    default: strcpy(return_string,"?"); break;
  } /* switch */



} /* easycom_serial_commmand */
#endif // FEATURE_EASYCOM_EMULATION





// --------------------------------------------------------------

#ifdef FEATURE_REMOTE_UNIT_SLAVE
void process_remote_slave_command(byte * slave_command_buffer, int slave_command_buffer_index, byte source_port, char * return_string){


/*
 *
 * This implements a protocol for host unit to remote unit communications
 *
 *
 * Remote Slave Unit Protocol Reference
 *
 *  PG - ping
 *  AZ - read azimuth  (returns AZxxx.xxxxxx)
 *  EL - read elevation (returns ELxxx.xxxxxx)
 *  RC - read coordinates (returns RC+xx.xxxx -xxx.xxxx)
 *  GS - query GPS status (returns GS0 (no sync) or GS1 (sync))
 *  DOxx - digital pin initialize as output;
 *  DIxx - digital pin initialize as input
 *  DPxx - digital pin initialize as input with pullup
 *  DRxx - digital pin read
 *  DLxx - digital pin write low
 *  DHxx - digital pin write high
 *  DTxxyyyy - digital pin tone output
 *  NTxx - no tone
 *  ARxx - analog pin read
 *  AWxxyyy - analog pin write
 *  SWxy - serial write byte
 *  SDx - deactivate serial read event; x = port #
 *  SSxyyyyyy... - serial write string; x = port #, yyyy = string of characters to send
 *  SAx - activate serial read event; x = port #
 *  RB - reboot
 *  CL - return clock date and time
 *
 * Responses
 *
 *  ER - report an error (remote to host only)
 *  EV - report an event (remote to host only)
 *  OK - report success (remote to host only)
 *  CS - report a cold start (remote to host only)
 *
 * Error Codes
 *
 *  ER01 - Serial port buffer timeout
 *  ER02 - Command syntax error
 *
 * Events
 *
 *  EVSxy - Serial port read event; x = serial port number, y = byte returned
 *
 *
 */



  byte command_good = 0;
  strcpy(return_string,"");
  char tempstring[25] = "";

  if (slave_command_buffer_index < 2) {
    strcpy(return_string,"ER02");  // we don't have enough characters - syntax error
  } else {

    #ifdef DEBUG_PROCESS_SLAVE
    debug_print("serial_serial_buffer: command_string: ");
    debug_print((char*)slave_command_buffer);
    debug_print("$ slave_command_buffer_index: ");
    debug_print_int(slave_command_buffer_index);
    debug_print("\n");
    #endif // DEBUG_PROCESS_SLAVE

    if (((slave_command_buffer[0] == 'S') && (slave_command_buffer[1] == 'S')) && (slave_command_buffer[2] > 47) && (slave_command_buffer[2] < 53)) { // this is a variable length command
      command_good = 1;
      for (byte x = 3; x < slave_command_buffer_index; x++) {
        switch (slave_command_buffer[2] - 48) {
          case 0: control_port->write(slave_command_buffer[x]); break;
          #if defined(FEATURE_MASTER_WITH_SERIAL_SLAVE)
          case 1: remote_unit_port->write(slave_command_buffer[x]); break;
          #endif
        }
      }
    }

    if (slave_command_buffer_index == 2) {

      #ifdef FEATURE_CLOCK
      if ((slave_command_buffer[0] == 'C') && (slave_command_buffer[1] == 'L')) {
        strcpy(return_string,"CL");
        update_time();
        strcat(return_string,clock_string());
        command_good = 1;
      }
      #endif //FEATURE_CLOCK


      #ifdef FEATURE_GPS
      if ((slave_command_buffer[0] == 'R') && (slave_command_buffer[1] == 'C')) {                    // RC - read coordinates
        strcpy(return_string,"RC");
        if (latitude < 0){strcat(return_string,"-");} else {strcat(return_string,"+");}
        dtostrf(abs(latitude),0,4,tempstring);
        strcat(return_string,tempstring);         
        strcat(return_string," ");
        if (longitude < 0){strcat(return_string,"-");} else {strcat(return_string,"+");}
        if (longitude < 100){strcat(return_string,"0");}
        dtostrf(abs(longitude),0,4,tempstring);
        strcat(return_string,tempstring);        
        command_good = 1;
      }   
      #ifdef FEATURE_CLOCK
      if ((slave_command_buffer[0] == 'G') && (slave_command_buffer[1] == 'S')) {                    // GS - query GPS sync
        strcpy(return_string,"GS");
        if (clock_status == GPS_SYNC){                
          strcat(return_string,"1");
        } else {
          strcat(return_string,"0");
        }        
        command_good = 1;
      }
      #endif //FEATURE_CLOCK                 
      #endif //FEATURE_GPS      

      if ((slave_command_buffer[0] == 'P') && (slave_command_buffer[1] == 'G')) {
        strcpy(return_string,"PG"); command_good = 1;
      }                                                                        // PG - ping
      if ((slave_command_buffer[0] == 'R') && (slave_command_buffer[1] == 'B')) {
        wdt_enable(WDTO_30MS); while (1) {
        }
      }                                                                        // RB - reboot
      if ((slave_command_buffer[0] == 'A') && (slave_command_buffer[1] == 'Z')) {
        strcpy(return_string,"AZ");
        //if ((raw_azimuth/HEADING_MULTIPLIER) < 1000) {
        //  strcat(return_string,"0");
        //}
        if ((raw_azimuth/HEADING_MULTIPLIER) < 100) {
          strcat(return_string,"0");
        }
        if ((raw_azimuth/HEADING_MULTIPLIER) < 10) {
          strcat(return_string,"0");
        }
        dtostrf(float(raw_azimuth/HEADING_MULTIPLIER),0,6,tempstring);
        strcat(return_string,tempstring);          
        command_good = 1;
      }
      #ifdef FEATURE_ELEVATION_CONTROL
      if ((slave_command_buffer[0] == 'E') && (slave_command_buffer[1] == 'L')) {
        strcpy(return_string,"EL");
        if ((elevation/HEADING_MULTIPLIER) >= 0) {
          strcat(return_string,"+");
        } else {
          strcat(return_string,"-");
        }
        //if (abs(elevation/HEADING_MULTIPLIER) < 1000) {
        //  strcat(return_string,"0");
        //}
        if (abs(elevation/HEADING_MULTIPLIER) < 100) {
          strcat(return_string,"0");
        }
        if (abs(elevation/HEADING_MULTIPLIER) < 10) {
          strcat(return_string,"0");
        }
        dtostrf(float(abs(elevation/HEADING_MULTIPLIER)),0,6,tempstring);
        strcat(return_string,tempstring);            
        command_good = 1;
      }
        #endif // FEATURE_ELEVATION_CONTROL
    } // end of two byte commands



    if (slave_command_buffer_index == 3) {
      if (((slave_command_buffer[0] == 'S') && (slave_command_buffer[1] == 'A')) & (slave_command_buffer[2] > 47) && (slave_command_buffer[2] < 53)) {
        serial_read_event_flag[slave_command_buffer[2] - 48] = 1;
        command_good = 1;
        strcpy(return_string,"OK");
      }
      if (((slave_command_buffer[0] == 'S') && (slave_command_buffer[1] == 'D')) & (slave_command_buffer[2] > 47) && (slave_command_buffer[2] < 53)) {
        serial_read_event_flag[slave_command_buffer[2] - 48] = 0;
        command_good = 1;
        strcpy(return_string,"OK");
      }

    }


    if (slave_command_buffer_index == 4) {
      if ((slave_command_buffer[0] == 'S') && (slave_command_buffer[1] == 'W')) { // Serial Write command
        switch (slave_command_buffer[2]) {
          case '0': control_port->write(slave_command_buffer[3]); command_good = 1; break;
          #if defined(FEATURE_MASTER_WITH_SERIAL_SLAVE)
          case '1': remote_unit_port->write(slave_command_buffer[3]); command_good = 1; break;
          #endif
        }
      }

      if ((slave_command_buffer[0] == 'D') && (slave_command_buffer[1] == 'O')) {
        if ((((slave_command_buffer[2] > 47) && (slave_command_buffer[2] < 58)) || (toupper(slave_command_buffer[2]) == 'A')) && (slave_command_buffer[3] > 47) && (slave_command_buffer[3] < 58)) {
          command_good = 1;
          byte pin_value = 0;
          if (toupper(slave_command_buffer[2]) == 'A') {
            pin_value = get_analog_pin(slave_command_buffer[3] - 48);
          } else {
            pin_value = ((slave_command_buffer[2] - 48) * 10) + (slave_command_buffer[3] - 48);
          }
          #ifdef DEBUG_PROCESS_SLAVE
          debug_print("service_serial_buffer: pin_value: ");
          debug_print_int(pin_value);
          #endif // DEBUG_PROCESS_SLAVE
          strcpy(return_string,"OK");
          pinModeEnhanced(pin_value, OUTPUT);
        }
      }

      if ((slave_command_buffer[0] == 'D') && (slave_command_buffer[1] == 'H')) {
        if ((((slave_command_buffer[2] > 47) && (slave_command_buffer[2] < 58)) || (toupper(slave_command_buffer[2]) == 'A')) && (slave_command_buffer[3] > 47) && (slave_command_buffer[3] < 58)) {
          command_good = 1;
          byte pin_value = 0;
          if (toupper(slave_command_buffer[2]) == 'A') {
            pin_value = get_analog_pin(slave_command_buffer[3] - 48);
          } else {
            pin_value = ((slave_command_buffer[2] - 48) * 10) + (slave_command_buffer[3] - 48);
          }
          digitalWriteEnhanced(pin_value, HIGH);
          strcpy(return_string,"OK");
        }
      }

      if ((slave_command_buffer[0] == 'D') && (slave_command_buffer[1] == 'L')) {
        if ((((slave_command_buffer[2] > 47) && (slave_command_buffer[2] < 58)) || (toupper(slave_command_buffer[2]) == 'A')) && (slave_command_buffer[3] > 47) && (slave_command_buffer[3] < 58)) {
          command_good = 1;
          byte pin_value = 0;
          if (toupper(slave_command_buffer[2]) == 'A') {
            pin_value = get_analog_pin(slave_command_buffer[3] - 48);
          } else {
            pin_value = ((slave_command_buffer[2] - 48) * 10) + (slave_command_buffer[3] - 48);
          }
          digitalWriteEnhanced(pin_value, LOW);
          strcpy(return_string,"OK");
        }
      }

      if ((slave_command_buffer[0] == 'D') && (slave_command_buffer[1] == 'I')) {
        if ((((slave_command_buffer[2] > 47) && (slave_command_buffer[2] < 58)) || (toupper(slave_command_buffer[2]) == 'A')) && (slave_command_buffer[3] > 47) && (slave_command_buffer[3] < 58)) {
          command_good = 1;
          byte pin_value = 0;
          if (toupper(slave_command_buffer[2]) == 'A') {
            pin_value = get_analog_pin(slave_command_buffer[3] - 48);
          } else {
            pin_value = ((slave_command_buffer[2] - 48) * 10) + (slave_command_buffer[3] - 48);
          }
          pinModeEnhanced(pin_value, INPUT);
          strcpy(return_string,"OK");
        }
      }

      if ((slave_command_buffer[0] == 'D') && (slave_command_buffer[1] == 'P')) {
        if ((((slave_command_buffer[2] > 47) && (slave_command_buffer[2] < 58)) || (toupper(slave_command_buffer[2]) == 'A')) && (slave_command_buffer[3] > 47) && (slave_command_buffer[3] < 58)) {
          command_good = 1;
          byte pin_value = 0;
          if (toupper(slave_command_buffer[2]) == 'A') {
            pin_value = get_analog_pin(slave_command_buffer[3] - 48);
          } else {
            pin_value = ((slave_command_buffer[2] - 48) * 10) + (slave_command_buffer[3] - 48);
          }
          pinModeEnhanced(pin_value, INPUT);
          digitalWriteEnhanced(pin_value, HIGH);
          strcpy(return_string,"OK");
        }
      }

      if ((slave_command_buffer[0] == 'D') && (slave_command_buffer[1] == 'R')) {
        if ((((slave_command_buffer[2] > 47) && (slave_command_buffer[2] < 58)) || (toupper(slave_command_buffer[2]) == 'A')) && (slave_command_buffer[3] > 47) && (slave_command_buffer[3] < 58)) {
          command_good = 1;
          byte pin_value = 0;
          if (toupper(slave_command_buffer[2]) == 'A') {
            pin_value = get_analog_pin(slave_command_buffer[3] - 48);
          } else {
            pin_value = ((slave_command_buffer[2] - 48) * 10) + (slave_command_buffer[3] - 48);
          }
          byte pin_read = digitalReadEnhanced(pin_value);
          strcpy(return_string,"DR");
          dtostrf((slave_command_buffer[2]-48),0,0,tempstring);
          strcat(return_string,tempstring);              
          dtostrf((slave_command_buffer[3]-48),0,0,tempstring);
          strcat(return_string,tempstring);  
          if (pin_read) {
            strcat(return_string,"1");
          } else {
            strcat(return_string,"0");
          }
        }
      }
      if ((slave_command_buffer[0] == 'A') && (slave_command_buffer[1] == 'R')) {
        if ((((slave_command_buffer[2] > 47) && (slave_command_buffer[2] < 58)) || (toupper(slave_command_buffer[2]) == 'A')) && (slave_command_buffer[3] > 47) && (slave_command_buffer[3] < 58)) {
          command_good = 1;
          byte pin_value = 0;
          if (toupper(slave_command_buffer[2]) == 'A') {
            pin_value = get_analog_pin(slave_command_buffer[3] - 48);
          } else {
            pin_value = ((slave_command_buffer[2] - 48) * 10) + (slave_command_buffer[3] - 48);
          }
          int pin_read = analogReadEnhanced(pin_value);
          strcpy(return_string,"AR");
          if (toupper(slave_command_buffer[2]) == 'A') {
            strcat(return_string,"A");
          } else {
            dtostrf((slave_command_buffer[2]-48),0,0,tempstring);
            strcat(return_string,tempstring);
          }
                        
          dtostrf((slave_command_buffer[3]-48),0,0,tempstring);
          strcat(return_string,tempstring);  
          if (pin_read < 1000) {
            strcat(return_string,"0");
          }
          if (pin_read < 100) {
            strcat(return_string,"0");
          }
          if (pin_read < 10) {
            strcat(return_string,"0");
          }
          dtostrf(pin_read,0,0,tempstring);
          strcat(return_string,tempstring);             
        }
      }

      if ((slave_command_buffer[0] == 'N') && (slave_command_buffer[1] == 'T')) {
        if ((((slave_command_buffer[2] > 47) && (slave_command_buffer[2] < 58)) || (toupper(slave_command_buffer[2]) == 'A')) && (slave_command_buffer[3] > 47) && (slave_command_buffer[3] < 58)) {
          command_good = 1;
          byte pin_value = 0;
          if (toupper(slave_command_buffer[2]) == 'A') {
            pin_value = get_analog_pin(slave_command_buffer[3] - 48);
          } else {
            pin_value = ((slave_command_buffer[2] - 48) * 10) + (slave_command_buffer[3] - 48);
          }
          noTone(pin_value);
          strcpy(return_string,"OK");
        }
      }

    } // if (slave_command_buffer_index == 4)

    if (slave_command_buffer_index == 7) {
      if ((slave_command_buffer[0] == 'A') && (slave_command_buffer[1] == 'W')) {
        if ((((slave_command_buffer[2] > 47) && (slave_command_buffer[2] < 58)) || (toupper(slave_command_buffer[2]) == 'A')) && (slave_command_buffer[3] > 47) && (slave_command_buffer[3] < 58)) {
          byte pin_value = 0;
          if (toupper(slave_command_buffer[2]) == 'A') {
            pin_value = get_analog_pin(slave_command_buffer[3] - 48);
          } else {
            pin_value = ((slave_command_buffer[2] - 48) * 10) + (slave_command_buffer[3] - 48);
          }
          int write_value = ((slave_command_buffer[4] - 48) * 100) + ((slave_command_buffer[5] - 48) * 10) + (slave_command_buffer[6] - 48);
          if ((write_value >= 0) && (write_value < 256)) {
            analogWriteEnhanced(pin_value, write_value);
            strcpy(return_string,"OK");
            command_good = 1;
          }
        }
      }
    }

    if (slave_command_buffer_index == 8) {
      if ((slave_command_buffer[0] == 'D') && (slave_command_buffer[1] == 'T')) {
        if ((((slave_command_buffer[2] > 47) && (slave_command_buffer[2] < 58)) || (toupper(slave_command_buffer[2]) == 'A')) && (slave_command_buffer[3] > 47) && (slave_command_buffer[3] < 58)) {
          byte pin_value = 0;
          if (toupper(slave_command_buffer[2]) == 'A') {
            pin_value = get_analog_pin(slave_command_buffer[3] - 48);
          } else {
            pin_value = ((slave_command_buffer[2] - 48) * 10) + (slave_command_buffer[3] - 48);
          }
          int write_value = ((slave_command_buffer[4] - 48) * 1000) + ((slave_command_buffer[5] - 48) * 100) + ((slave_command_buffer[6] - 48) * 10) + (slave_command_buffer[7] - 48);
          if ((write_value >= 0) && (write_value <= 9999)) {
            tone(pin_value, write_value);
            strcpy(return_string,"OK");
            command_good = 1;
          }
        }
      }
    }


    if (!command_good) {
      strcpy(return_string,"ER0289");
    }
  }

  slave_command_buffer_index = 0;

}
#endif //FEATURE_REMOTE_UNIT_SLAVE

// --------------------------------------------------------------
void port_flush(){

  
  #if defined(CONTROL_PORT_MAPPED_TO) && (defined(FEATURE_REMOTE_UNIT_SLAVE) || defined(FEATURE_YAESU_EMULATION) || defined(FEATURE_EASYCOM_EMULATION))
  control_port->flush();
  #endif //CONTROL_PORT_MAPPED_TO

  #if defined(FEATURE_MASTER_WITH_SERIAL_SLAVE)
  remote_unit_port->flush();
  #endif

  #if defined(GPS_PORT_MAPPED_TO) && defined(FEATURE_GPS)
  gps_port->flush();
  #endif //defined(GPS_PORT_MAPPED_TO) && defined(FEATURE_GPS)
  

}
// --------------------------------------------------------------
#ifdef FEATURE_POWER_SWITCH
void service_power_switch(){

  static byte power_switch_state = 1;

  #ifdef FEATURE_ELEVATION_CONTROL
  if ((az_state != IDLE) || (el_state != IDLE)){
    last_activity_time = millis();
  }
  #else //FEATURE_ELEVATION_CONTROL
  if (az_state != IDLE){
    last_activity_time = millis();
  }
  #endif //FEATURE_ELEVATION_CONTROL


  if ((millis()-last_activity_time) > (60000 * POWER_SWITCH_IDLE_TIMEOUT)) {
    if (power_switch_state){ 
      digitalWriteEnhanced(power_switch, LOW);
      power_switch_state = 0;
    }
  } else {
    if (!power_switch_state){ 
      digitalWriteEnhanced(power_switch, HIGH);
      power_switch_state = 1;
    }
  }


}
#endif //FEATURE_POWER_SWITCH


//------------------------------------------------------
char *coordinates_to_maidenhead(float latitude_degrees,float longitude_degrees){

  static char temp_string[8] = "";  // I had to declare this static in Arduino 1.6, otherwise this won't work (it worked before)

  latitude_degrees += 90.0;
  longitude_degrees += 180.0;

  temp_string[0] = (int(longitude_degrees/20)) + 65;
  temp_string[1] = (int(latitude_degrees/10)) + 65;
  temp_string[2] = (int((longitude_degrees - int(longitude_degrees/20)*20)/2)) + 48;
  temp_string[3] = (int(latitude_degrees - int(latitude_degrees/10)*10)) + 48;
  temp_string[4] = (int((longitude_degrees - (int(longitude_degrees/2)*2)) / (5.0/60.0))) + 97;
  temp_string[5] = (int((latitude_degrees - (int(latitude_degrees/1)*1)) / (2.5/60.0))) + 97;
  temp_string[6] = 0;

  return temp_string;

}
//------------------------------------------------------
#ifdef FEATURE_MASTER_WITH_ETHERNET_SLAVE
byte ethernet_slave_link_send(char * string_to_send){

  if (ethernetslavelinkclient0_state == ETHERNET_SLAVE_CONNECTED){
    ethernetslavelinkclient0.print(ETHERNET_PREAMBLE);
    ethernetslavelinkclient0.println(string_to_send);
    #ifdef DEBUG_ETHERNET
    debug_print("ethernet_slave_link_send: ");
    debug_println(string_to_send);
    #endif //DEBUG_ETHERNET
    return 1;
  } else {
    #ifdef DEBUG_ETHERNET
    debug_print("ethernet_slave_link_send: link down not sending:");
    debug_println(string_to_send);
    #endif //DEBUG_ETHERNET    
    return 0;
  }

}
#endif //FEATURE_MASTER_WITH_ETHERNET_SLAVE

//------------------------------------------------------
#if defined(OPTION_SYNC_MASTER_COORDINATES_TO_SLAVE) && (defined(FEATURE_MASTER_WITH_SERIAL_SLAVE) || defined(FEATURE_MASTER_WITH_ETHERNET_SLAVE))
void sync_master_coordinates_to_slave(){

  static unsigned long last_sync_master_coordinates_to_slave = 10000;

  if ((millis() - last_sync_master_coordinates_to_slave) >= (SYNC_MASTER_COORDINATES_TO_SLAVE_SECS * 1000)){
    if (submit_remote_command(REMOTE_UNIT_RC_COMMAND, 0, 0)) {
      #ifdef DEBUG_SYNC_MASTER_COORDINATES_TO_SLAVE
      debug_println("sync_master_coordinates_to_slave: submitted REMOTE_UNIT_RC_COMMAND");
      #endif //DEBUG_SYNC_MASTER_COORDINATES_TO_SLAVE
      last_sync_master_coordinates_to_slave = millis();  
    }  
  }


}
#endif //defined(OPTION_SYNC_MASTER_COORDINATES_TO_SLAVE) && (defined(FEATURE_MASTER_WITH_SERIAL_SLAVE) || defined(FEATURE_MASTER_WITH_ETHERNET_SLAVE))
//------------------------------------------------------

#if defined(FEATURE_CLOCK) && defined(OPTION_SYNC_MASTER_CLOCK_TO_SLAVE) && (defined(FEATURE_MASTER_WITH_SERIAL_SLAVE) || defined(FEATURE_MASTER_WITH_ETHERNET_SLAVE))
void sync_master_clock_to_slave(){

  static unsigned long last_sync_master_clock_to_slave = 5000;

  if ((millis() - last_sync_master_clock_to_slave) >= (SYNC_MASTER_CLOCK_TO_SLAVE_CLOCK_SECS * 1000)){
    if (submit_remote_command(REMOTE_UNIT_CL_COMMAND, 0, 0)) {
      #ifdef DEBUG_SYNC_MASTER_CLOCK_TO_SLAVE
      debug_println("sync_master_clock_to_slave: submitted REMOTE_UNIT_CL_COMMAND");
      #endif //DEBUG_SYNC_MASTER_CLOCK_TO_SLAVE
      last_sync_master_clock_to_slave = millis();  
    }  
  }

  // if REMOTE_UNIT_CL_COMMAND above was successful, issue a GS (query GPS sync command) to get GPS sync status on the remote
  if (clock_synced_to_remote){
    if (submit_remote_command(REMOTE_UNIT_GS_COMMAND, 0, 0)) {
      #ifdef DEBUG_SYNC_MASTER_CLOCK_TO_SLAVE
      debug_println("sync_master_clock_to_slave: submitted REMOTE_UNIT_GS_COMMAND");
      #endif //DEBUG_SYNC_MASTER_CLOCK_TO_SLAVE
      clock_synced_to_remote = 0; 
    }      
  }

}
#endif //defined(FEATURE_CLOCK) && defined(OPTION_SYNC_MASTER_CLOCK_TO_SLAVE)

//------------------------------------------------------
#if defined(FEATURE_STEPPER_MOTOR)
void service_stepper_motor_pulse_pins(){

  service_stepper_motor_pulse_pins_count++;

  static unsigned int az_stepper_pin_transition_counter = 0;
  static byte az_stepper_pin_last_state = LOW;

  if (az_stepper_freq_count > 0){
    az_stepper_pin_transition_counter++;
    if (az_stepper_pin_transition_counter >= az_stepper_freq_count){
      if (az_stepper_pin_last_state == LOW){
        digitalWrite(az_stepper_motor_pulse,HIGH);
        az_stepper_pin_last_state = HIGH;
      } else {
        digitalWrite(az_stepper_motor_pulse,LOW);
        az_stepper_pin_last_state = LOW;
      }
      az_stepper_pin_transition_counter = 0;
    }
  } else {
    az_stepper_pin_transition_counter = 0;
  }

  #ifdef FEATURE_ELEVATION_CONTROL
  static unsigned int el_stepper_pin_transition_counter = 0;
  static byte el_stepper_pin_last_state = LOW;

  if (el_stepper_freq_count > 0){
    el_stepper_pin_transition_counter++;
    if (el_stepper_pin_transition_counter >= el_stepper_freq_count){
      if (el_stepper_pin_last_state == LOW){
        digitalWrite(el_stepper_motor_pulse,HIGH);
        el_stepper_pin_last_state = HIGH;
      } else {
        digitalWrite(el_stepper_motor_pulse,LOW);
        el_stepper_pin_last_state = LOW;
      }
      el_stepper_pin_transition_counter = 0;
    }
  } else {
    el_stepper_pin_transition_counter = 0;
  }

  #endif //FEATURE_ELEVATION_CONTROL

}
#endif //defined(FEATURE_STEPPER_MOTOR)

//------------------------------------------------------
#ifdef FEATURE_STEPPER_MOTOR
void set_az_stepper_freq(unsigned int frequency){

  if (frequency > 0) {
    az_stepper_freq_count = 2000 / frequency;
  } else {
    az_stepper_freq_count = 0;
  }

  #ifdef DEBUG_STEPPER
  debug_print("set_az_stepper_freq: ");
  debug_print_int(frequency);
  debug_print(" az_stepper_freq_count:");
  debug_print_int(az_stepper_freq_count);
  debug_println("");
  #endif //DEBUG_STEPPER

}

#endif //FEATURE_STEPPER_MOTOR
//------------------------------------------------------
#if defined(FEATURE_ELEVATION_CONTROL) && defined(FEATURE_STEPPER_MOTOR)
void set_el_stepper_freq(unsigned int frequency){


  if (frequency > 0) {
    el_stepper_freq_count = 2000 / frequency;
  } else {
    el_stepper_freq_count = 0;
  }

  #ifdef DEBUG_STEPPER
  debug_print("set_el_stepper_freq: ");
  debug_print_int(frequency);
  debug_print(" el_stepper_freq_count:");
  debug_print_int(el_stepper_freq_count);
  debug_println("");
  #endif //DEBUG_STEPPER

}

#endif //defined(FEATURE_ELEVATION_CONTROL) && defined(FEATURE_STEPPER_MOTOR)





//-------------------------------------------------------
#ifdef FEATURE_ANALOG_OUTPUT_PINS
void service_analog_output_pins(){

  static int last_azimith_voltage_out = 0;
  int azimuth_voltage_out = map(azimuth/HEADING_MULTIPLIER,0,360,0,255);
  if (last_azimith_voltage_out != azimuth_voltage_out){
    analogWriteEnhanced(pin_analog_az_out,azimuth_voltage_out);
    last_azimith_voltage_out = azimuth_voltage_out;
  }

  #ifdef FEATURE_ELEVATION_CONTROL
  static int last_elevation_voltage_out = 0;
  int elevation_voltage_out = map(elevation/HEADING_MULTIPLIER,0,ANALOG_OUTPUT_MAX_EL_DEGREES,0,255);
  if (last_elevation_voltage_out != elevation_voltage_out){
    analogWriteEnhanced(pin_analog_el_out,elevation_voltage_out);
    last_elevation_voltage_out = elevation_voltage_out;
  }
  #endif //FEATURE_ELEVATION_CONTROL

}
#endif //FEATURE_ANALOG_OUTPUT_PINS



//-------------------------------------------------------
#if defined(FEATURE_SUN_PUSHBUTTON_AZ_EL_CALIBRATION) && defined(FEATURE_SUN_TRACKING)
void check_sun_pushbutton_calibration(){

  static unsigned long last_update_time = 0;

  if ((digitalReadEnhanced(pin_sun_pushbutton_calibration) == LOW) && ((millis() - last_update_time) > 500)){
    update_sun_position();
    if (calibrate_az_el(sun_azimuth, sun_elevation)) {
    } else {
    }
    last_update_time = millis();
  }

}
#endif //defined(FEATURE_SUN_PUSHBUTTON_AZ_EL_CALIBRATION) && defined(FEATURE_SUN_TRACKING)       

//-------------------------------------------------------

#if defined(FEATURE_MOON_PUSHBUTTON_AZ_EL_CALIBRATION) && defined(FEATURE_MOON_TRACKING)
void check_moon_pushbutton_calibration(){

  static unsigned long last_update_time = 0;

  if ((digitalReadEnhanced(pin_moon_pushbutton_calibration) == LOW) && ((millis() - last_update_time) > 500)){
    update_moon_position();
    if (calibrate_az_el(moon_azimuth, moon_elevation)) {
    } else {
    }
    last_update_time = millis();
  }

}
#endif //defined(FEATURE_MOON_PUSHBUTTON_AZ_EL_CALIBRATION) && defined(FEATURE_MOON_TRACKING)       

//-------------------------------------------------------
#ifdef FEATURE_AUTOCORRECT
void submit_autocorrect(byte axis,float heading){

  #ifdef DEBUG_AUTOCORRECT
  debug_print("submit_autocorrect: ");
  #endif //DEBUG_AUTOCORRECT

  if (axis == AZ){
    autocorrect_state_az = AUTOCORRECT_WATCHING_AZ;
    autocorrect_az = heading;
    autocorrect_az_submit_time = millis();

    #ifdef DEBUG_AUTOCORRECT
    debug_print("AZ: ");
    #endif //DEBUG_AUTOCORRECT

  }


  #ifdef FEATURE_ELEVATION_CONTROL
  if (axis == EL){
    autocorrect_state_el = AUTOCORRECT_WATCHING_EL;
    autocorrect_el = heading;
    autocorrect_el_submit_time = millis();

    #ifdef DEBUG_AUTOCORRECT
    debug_print("EL: ");
    #endif //DEBUG_AUTOCORRECT

  }
  #endif //FEATURE_ELEVATION_CONTROL

  #ifdef DEBUG_AUTOCORRECT
  debug_print_float(heading,2);
  debug_println("");
  #endif //DEBUG_AUTOCORRECT

}
#endif //FEATURE_AUTOCORRECT

//-------------------------------------------------------

// that's all, folks !