/**************************************************************************
Name            - main()
Parameters      - Nothing
Responsibility  - Main control structure for the AZ/EL rotor control system
                
Notes           - Initial Development environment:
                    OS  LINUX FC5 with updates
                    FSF avr-gcc Compiler version 3.4.5
                    avr-libc version 1.4.5
                    avrdude version 4.4.0

                - Current Development environment:
                    OS  LINUX Fedora CorFedora Core
                    FSF avr-gcc Compiler version 4.2.2
                    avr-libc version 
                    avrdude version 5.8

                - Controller -- Atmel ATmega16 with 8.0 Mhz external resonator
                                Use with different resonator freq. will require
                                code changes.

                                An Atmega32 will also work but change the
                                Makefile to reflect the different part.

                                Note: The interrupt vector tables are not the
                                same for the 16 & 32 part so you need to
                                recompile!

                - FUSE BITS REQUIRED:
                    HFUSE = D9
                    LFUSE = FF

		    parport0 may need to have its mode changed
                    before using avrdude in terminal mode.
                         chmod 0666 /dev/parport0

                    Verif fuse bits (for Armega16 part) with:
                    avrdude -c dt006 -p m16 -P /dev/parport0 -t

                    Verif fuse bits (for Armega32 part) with:
                    avrdude -c dt006 -p m32 -P /dev/parport0 -t

                        read hfuse
                        read lfuse

                        write hfuse 00 0xd9
                        write lfuse 00 0xff

                        quit    - to exit avrdude in terminal mode

                 
Returns         - Nothing
Author          - Bill (N9CX)
**************************************************************************/
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <avr/eeprom.h>


#include "uart_putchar.h"
#include "gpr.h"
#include "lcd.h"
#include "switches.h"
#define LOCAL
#include "main.h"
#include "sw_timers.h"
#include "uart_getchar.h"

void init_uart(void );
int out_queue(void);
void initialize(void);
void mode_priority(void);
void save_el_rotor(void);
void save_az_rotor(void);
void manual(struct rotor *);
void rotor_pwr(struct rotor *, char);
void calibrate(struct rotor *);
void init_timer1(void);
void init_rotor_ctl_io(void);
void rotor_bearing(struct rotor *);
void auto_track(struct rotor *);
void detect_rotor(struct rotor *);
char novacomm1_parser(char *cptr, int);

int lcd_putc(char c, FILE * stream);
/*
This is one of the  new avr-libc options for stdio I/O
It opens the file structures and returns a pointer to it.
This happens prior to main being called.
*/
int uart_putchar(char c, FILE * stream);
int uart_getchar(char c, FILE * stream);

FILE uart_out = FDEV_SETUP_STREAM(uart_putchar,
                uart_getchar,
                _FDEV_SETUP_RW);

FILE lcd_out =  FDEV_SETUP_STREAM(lcd_putc,
                NULL,
                _FDEV_SETUP_WRITE);

char rotor_position(struct rotor *);
void update_display(void);

extern char new_auto_bearing;
int main(void) {

    char parse_result=0;
    mode = INITIALIZE;

    while(1) {
        switch( mode ) {
          case DETECT:
                detect_rotor(&az_rotor);
                detect_rotor(&el_rotor);
                if(az_rotor.detect_submode == DONE &&
                   el_rotor.detect_submode == DONE){
                        az_rotor.detect_submode = START;
                        el_rotor.detect_submode = START;
                        request_lcd_refresh = TRUE;
                        mode = MANUAL;
                }
              break;
            case MANUAL:
                if(az_rotor.type == 'P') manual(&az_rotor);
                if(el_rotor.type == 'P') manual(&el_rotor);
                break;
            case AUTO:
                if(!az_rotor.first_pass && !el_rotor.first_pass){
                    if(new_auto_bearing){
                        parse_result = novacomm1_parser(rx_buffer,
                                                        sizeof(rx_buffer));
                        new_auto_bearing = FALSE;
                        request_lcd_refresh = TRUE;
                    }
                }
                if(az_rotor.type == 'P') auto_track(&az_rotor);
                else az_rotor.first_pass = FALSE;

                if(el_rotor.type == 'P') auto_track(&el_rotor);
                else el_rotor.first_pass = FALSE;

                break;
            case CALIBRATE:
                if(az_rotor.type == 'P') calibrate(&az_rotor);
                if(el_rotor.type == 'P') calibrate(&el_rotor);

                if( (el_rotor.cal_submode == FINISHED||el_rotor.type!='P') &&
                   (az_rotor.cal_submode == FINISHED|| az_rotor.type!='P') ){
                    if(el_rotor.type =='P') save_el_rotor();
                    if(az_rotor.type =='P') save_az_rotor();
                    request_lcd_refresh = TRUE;
                    mode = MANUAL;
                }
                break;
            case IDLE:
                break;
            case INITIALIZE: //Blocks till finished
                initialize();
                sei(); // enable global interrupts

                /* Changed "and" to "or" for type detection 6-19-2009 */
                //if(el_rotor.type != 'P' && az_rotor.type != 'P'){
                if(el_rotor.type != 'P' || az_rotor.type != 'P'){
                    mode = DETECT;
                }
                else {
                    mode = MANUAL;
                }
                request_lcd_refresh = TRUE;
                break;
            default:
               mode = MANUAL;
               break;
        }
        if(mode == MANUAL || mode == AUTO || mode == DETECT){
            if(rotor_position(&az_rotor)){
                rotor_bearing(&az_rotor);
                request_lcd_refresh = TRUE;
            }
            if(rotor_position(&el_rotor)){
                rotor_bearing(&el_rotor);
                request_lcd_refresh = TRUE;
            }
        }
        if(request_lcd_refresh){
            update_display();
            request_lcd_refresh = FALSE;
        }
        // Check for time out of the azimuth pulse limit timer
        if(!az_rotor.pulse_timer){
            if(az_rotor.moving){
                if(az_rotor.cw) az_rotor.limit = CW; // TO in CW direction
                else az_rotor.limit = CCW;           // TO in CCW direction
                az_rotor.pwr_ctl(&az_rotor, PWR_OFF);
            }
            else{
                // not moving so reset the timeout
                az_rotor.pulse_timer = PULSE_TIMEOUT;
            }
        }
        // Check for time out of the elevation pulse limit timer
        if(!el_rotor.pulse_timer){
            if(el_rotor.moving){
                if(el_rotor.cw) el_rotor.limit = CW; // TO in CW direction
                else el_rotor.limit = CCW;           // TO in CCW direction
                el_rotor.pwr_ctl(&el_rotor, PWR_OFF);
            }
            else{
                // not moving so reset the timeout
                el_rotor.pulse_timer = PULSE_TIMEOUT;
            }
        }
        // This can change the current "mode"
        mode_priority();
#define DEBUG
#ifdef DEBUG
        if(mode != old_mode ) {
            fprintf(&uart_out, "%2.d ", mode);
            old_mode = mode;
        }
#endif

    
    }
}


/**************************************************************************
Name            -rotor_pwr
Parameters      - The address of a rotor structure (az_rotor or el_rotor)
                  char specifying the desired action
Responsibility  - Manages the power to a rotor motor
                
Notes           - The function has evolved over time.
                  It uses data in the structure pointed to by the first
                  parameter to know how to manipulate power to the rotor.
                  Encapsulating the details fo power management in the 
                  rotor structure enables it to work for both the azmith
                  and elevation rotors.

                  Although not necessary at this time, the rotor structure
                  contains the address of the function that manages power 
                  for it (this function address). Various data are loaded into
                  the rotor sturctures at startup by the initialize() function.
                  Bit masks for power management is stored in the rotor
                  structure also. It is an attempt to enable a single function
                  (this one) to manage power for both the azmith and
                  elevation rotors.
                  power.

                
Returns         - void (Nothing)
Author          - Bill (N9CX)
**************************************************************************/
void rotor_pwr( struct rotor *p_rotor, char ctl){
    switch(ctl){
      case PWR_CW:
            PORTB &= p_rotor->pwr_cw; // 0 to pin 0 of PORT B
            p_rotor->moving = TRUE;
            p_rotor->cw = TRUE;
            p_rotor->ccw = FALSE;
          break;
      case PWR_CCW:
            PORTB &= p_rotor->pwr_ccw; // 0 to pin 1 of PORT B
            p_rotor->moving = TRUE;
            p_rotor->ccw = TRUE;
            p_rotor->cw = FALSE;
          break;
      case PWR_OFF:
            // 1 to both  pins to make sure both SS relays are off
            PORTB |= p_rotor->pwr_off;
            if(p_rotor == &el_rotor) {
                save_el_rotor();
            }
            if(p_rotor == &az_rotor) {
                save_az_rotor();
            }
            p_rotor->moving = FALSE;
          break;
    }
}


void save_az_rotor(void) {
    eeprom_write_block (&az_rotor, &ee_az_rotor, sizeof(struct rotor));
}

void save_el_rotor(void) {
    eeprom_write_block (&el_rotor, &ee_el_rotor, sizeof(struct rotor));
}
