/** INSTRUCTION: ********************************
    after a station is tuned,
    call rds_prepare() once,
    then loop in the rds_loop().
*************************************************/

// RDA5807 registers: ----------------------------------
typedef enum {
  RDA5807_REG_CHIPID          = 0x00,
  RDA5807_REG_CTRL            = 0x02,
  RDA5807_REG_CHANNEL         = 0x03,
  RDA5807_REG_IOCFG           = 0x04,
  RDA5807_REG_INTM_THRESH_VOL = 0x05,
#ifdef RDS_EXT_REGS  //RDA5807M has additional registers 0x06, 0x07, 0x08
  RDA5807_REG_06              = 0x06,
  RDA5807_REG_EXT_BAND        = 0x07,  //can tune in an extended band 50..115MHz
  RDA5807_REG_08              = 0x08,  //frequency in kHz for direct input mode
#endif
  RDA5807_REG_STATUS          = 0x0A,
  RDA5807_REG_SIGNAL          = 0x0B,
#ifdef RDS_SUPPORTED
  //In RDA5807P (which doesn't have RDS) values in the four RDS registers 0x0C..0x0F never change.
  RDA5807_REG_RDS_A           = 0x0C,  // PI-code (country#, zone#), station#
  RDA5807_REG_RDS_B           = 0x0D,  // data group type (0=name, 2=text, 4=time), text position
  RDA5807_REG_RDS_C           = 0x0E,  // data (two ASCII-characters)
  RDA5807_REG_RDS_D           = 0x0F   // data (two ASCII-characters)
#endif
} RD5807_REG_T;

//---------------------------------------------------------------------
uint16_t rda5807_status;
uint16_t rda5807_signal;

uint16_t rda5807_rds_A, rda5807_rds_B, rda5807_rds_C, rda5807_rds_D;
uint16_t rda5807_rds_PI=0x0000;
uint8_t  rda5807_rds_PTY=0, rda5807_rds_grouptype, rda5807_rds_textpos;

uint8_t rds_buf_t0[8+1];   //group type 0 (station name): 8 characters
uint8_t rds_buf_t2[64+1];  //group type 2 (radiotext): 64 characters

//#define RDS_RAW_JDATE
uint32_t jdate;  // 17-bit modified Julian date
uint8_t rds_hours, rds_minutes;
signed char local_offset;  //in units of half-hour, with a sign


//--- Prototypes: -------------------------------------------------------------------
uint16_t rda5807_read_reg(uint8_t reg16bit_index);  //read a 16-bit register out of RDA5807, swap bytes
void rda5807_write_reg(uint8_t reg16bit_index, uint16_t reg_val);  //write a 16-bit value to a register of RDA5807
static void rda5807_process_one_char(uint8_t chr, uint8_t *textbuf, uint8_t lcd_row)
void rda5807_process_rds_data()  //print group 0 in line 4, group 2 in lines 5-8, group 4 in line 9

//-----------------------------------------------------------------------------------
void rds_prepare()
{
  Lcd_GotoXY(9, 4);  //line 4
  Lcd_print("PI=xxxx PTY=");  // 16-bit ProgramInformation code, 5-bit ProgramTYpe code (mask 0x03E0)
  Lcd_GotoXY(0, 9);  //lines 9,[10],11-15
#ifdef RDS_RAW_JDATE
  Lcd_print("Jdate:");   //line 9 - date, time, local time offset
#endif

  memset(rds_buf_t0,  8, ' ');
  memset(rds_buf_t2, 64, ' ');
}

//-----------------------------------------------------------------------------------
void rds_loop()
{
  rda5807_status = rda5807_read_reg(RDA5807_REG_STATUS);
  rda5807_signal = rda5807_read_reg(RDA5807_REG_SIGNAL);
  if ( ((rda5807_status & 0x9000) == 0x9000) &&  //RDS_Ready and RDS_Sync
      !(rda5807_signal & 0x000F)) {  //BLERA=BLERB=0, no errors in rds_A and rds_B
    rda5807_rds_A = rda5807_read_reg(RDA5807_REG_RDS_A);
    rda5807_rds_B = rda5807_read_reg(RDA5807_REG_RDS_B);
    rda5807_rds_C = rda5807_read_reg(RDA5807_REG_RDS_C);
    rda5807_rds_D = rda5807_read_reg(RDA5807_REG_RDS_D);
  
    rda5807_process_rds_data();
  }
}

//-----------------------------------------------------------------------------------
void rda5807_process_rds_data()  //print group 0 in line 4, group 2 in lines 5-8, group 4 in line 9
{
uint8_t pty_code = (rda5807_rds_B >> 5) & 0x1F;
uint8_t *textbuf;

  if (rda5807_rds_A != rda5807_rds_PI) {
    rda5807_rds_PI = rda5807_rds_A;
    Lcd_GotoXY(12, 4);  //print 16-bit ProgramInformation code in line 4
    Lcd_hex16bit(rda5807_rds_PI);  //nibbles: country, coverage, prog#, prog#
  }

  if (pty_code != rda5807_rds_PTY) {
    rda5807_rds_PTY = pty_code;
    Lcd_GotoXY(21, 4);  //print 5-bit ProgramTYpe code (mask 0x03E0) in line 4
    Lcd_dec99zr(rda5807_rds_PTY);  //print in decimal format with leading zeros as "00".."31"
  }

  rda5807_rds_grouptype = (rda5807_rds_B >> 8) & 0xF8;  //group type (4-bit) and variant A/B (1-bit)

  switch (rda5807_rds_grouptype) {
    case 0x00: case 0x08:  //group type 0 - station name, 1*2*(1<<2)=8 chars
      rda5807_rds_textpos = (rda5807_rds_B & 0x03) * 2;  //position of a 2-char chunk in a string
      textbuf = &rds_buf_t0[rda5807_rds_textpos];

      rda5807_process_one_char((rda5807_rds_D >> 8), textbuf, 4);
      textbuf++;
      rda5807_process_one_char( rda5807_rds_D,       textbuf, 4);
      break;
    case 0x20:  //group type 2, var.A - radiotext, 2*2*(1<<4)=64 chars
    //case 0x28:  //group type 2, var.B - radiotext, 1*2*(1<<4)=32 chars
      rda5807_rds_textpos = (rda5807_rds_B & 0x0F) * 4;  //position of a 4-char chunk in a string
      textbuf = &rds_buf_t2[rda5807_rds_textpos];

      rda5807_process_one_char((rda5807_rds_C >> 8), textbuf, 5);
      textbuf++;
      rda5807_process_one_char( rda5807_rds_C,       textbuf, 5);
      textbuf++;
      rda5807_process_one_char((rda5807_rds_D >> 8), textbuf, 5);
      textbuf++;
      rda5807_process_one_char( rda5807_rds_D,       textbuf, 5);
      break;
    case 0x40:  //group type 4, var.A - time and date, transmitted at the start of every minute
      jdate = ((((uint32_t)rda5807_rds_B & 0x0003) << 15) |
               ( (uint32_t)rda5807_rds_C           >>  1));
      rds_hours = (rda5807_rds_D >> 12);
        if (rda5807_rds_C & 0x0001)  rds_hours |= 0x10;
      rds_minutes = (rda5807_rds_D >> 6) & 0x3F;
      local_offset = (rda5807_rds_D & 0x1F);
        if (rda5807_rds_D & 0x20)  local_offset = -local_offset;
#ifdef RDS_RAW_JDATE
      //print in format: "Jdate:99999  23:59 +4"
      Lcd_GotoXY(6, 9);
      Lcd_dec00009(jdate, 5);  //print in decimal format with leading zeros as "00000".."99999"
#else  // convert JDate to Year, Month, Day of month, Day of week
      {
        float Y_f, M_f;
        uint32_t rds_year, rds_month, rds_day;

        Y_f = ((float)jdate - 15078.2F) / 365.25F;  //Y' = int((MJD - 15078.2) / 365.25)
        rds_year = (uint32_t)Y_f;
        M_f = ((float)jdate - 14956.1F - (uint32_t)((float)rds_year * 365.25F)) / 30.6001F;  //M' = int((MJD - 14956.1 - int(Y' * 365.25)) / 30.6001)
        rds_month = (uint32_t)M_f;
        rds_day = jdate - 14956 - (uint32_t)((float)rds_year * 365.25F) - (uint32_t)((float)rds_month * 30.6001F);  //D = MJD - 14956 - int(Y' * 365.25) - int(M' * 30.6001)
        rds_month--;
        if  (rds_month==14 || rds_month==15) {  //if M' = 14 or M' = 15, then K = 1; else K = 0
          rds_year++;       //Y = Y' + K
          rds_month -= 12;  //M = M' - 1 - K*12
        }
        rds_year -= 100;  //calculated value since year 1900; convert to a byte value, since year 2000
        //print in format: "31-12-2013/w 23:59 +4", where w=weekday=(1..7)
        Lcd_GotoXY(0, 9);
        Lcd_date_time(rds_day, rds_month, rds_year, '-');
        Lcd_putc('/');  Lcd_hex4bit(((jdate + 2) % 7) + 1);  //WD = ((MJD + 2) mod 7) + 1
      }
#endif
      Lcd_GotoXY(13, 9);
      Lcd_date_time(rds_hours, rds_minutes, 0, ':');
      Lcd_GotoXY(18, 9);
      Lcd_putc(' ');  Lcd_putc((local_offset < 0) ? '-' : '+');
      Lcd_hex4bit((local_offset >> 1) & 0x0F);  //assume that the offset is in range -9..+9 hours
      Lcd_putc('h');
      break;
  }
}

//-----------------------------------------------------------------------------------
static void rda5807_process_one_char(uint8_t chr, uint8_t *textbuf, uint8_t lcd_row)
{
  if ((chr >= 0x20) && (chr <= 0x7A) &&  //it must be a printable character in ASCII-range
      (chr != *textbuf)) {
    *textbuf = chr;  //update one character and print it
    Lcd_GotoXY((rda5807_rds_textpos % 16), (lcd_row + (rda5807_rds_textpos / 16)));
    Lcd_putc(chr);
  }
  rda5807_rds_textpos++;
}
