Converting IRIG-B into NMEA String

Hello to everyone.
I've realized the code reported down here with my limited knowledge.
I'd like to improve it with your knowledge, making it more elegant/efficient or just to learn if there are better ways to code it.

The problem:
I've an IRIG GENERATOR that provide 100bps of data in the IRIG-B (standard 200-04). I need to convert it in a NMEA string and send out on the serial port.
I will then use a RS232 shield to converter and send to the instruments requiring that string.

Bitstream source
100 bps. I have 3 type of square impulses with different duration:
8ms POINTER
5ms HIGH
2ms LOW
Each information is divided in hundres/tens/units presented in BCD starting with the LSB.

See IRIG-B004 for more info on the exact corrispondence between bits and data fields.


The PPS signal (on a separate channel) corresponds to two consecutive POINTER pulse (P9+P0).

NMEA ZDA String output
I need to build a NMEA string and send it on the serial before 0.5 sec from the PPS signal (on a separate line) corresponding to th3 first P0 bit of the string.
The desired string is the following:
$GPZDAhhmmss.ss,Zm,Zh,yyyy,mm,dd*ch
where:
-first part is the time (with the hundredth of seconds from the PPS)
-Zm and Zh are the time parametres for the zone
-date fields follow
-ch is the checksum, it's the HEX obtained by the XOR of all the char between $ and * (excluded)

Led indicator
There is a led indicating when the arduino send the string on the serial

The code

/***** ARDUIRIG *****/
/* this code converts a IRIG.B bit sequence to a NMEA string and send it oou using the serial port */
byte pin = 3;              //input pin for IRIG-B bit sequence
byte ledPin = 8;          //led used as serial stream indicator
bool ledState = 0;

/* Flag for the various check */
boolean PPD_flag;
bool dataFlag = 0;
bool timeFlag = 0;
bool monthFlag;
bool yearFlag = 0;
bool serialFlag = 0;

/*** Variables to desume from the IRIG-B stream ***/
byte Seconds;
byte Minutes;
byte Hours;
int DaysOY;              //day of the year (given by IRIG-B)
int Days_old;             //day of the year at the previous interval
int Days;                   //day of the month
byte Month;
unsigned int Year;
unsigned int SBS;         // straight binary seconds (17 bits)
/*** Reiceved bits counter and array ***/
byte counter;              //reiceved bit counter[max 84+5 INDEX]
byte bitstream[89];     //array where input bitstream is stHoursd [max 84 + 5 INDEX]
/*** Program Variables ***/
unsigned long duration;
unsigned long PPDmillisec;
byte daysInMonth[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
/*** STRING PARAMETERS ***/
byte TMZh = 0;
byte TMZm = 0;

void setup()
{
  pinMode(pin, INPUT);
  pinMode(ledPin, OUTPUT);
  Serial.begin(4800, SERIAL_8N1);
}

void loop()
{
  duration = pulseIn(pin, HIGH, 250000);      //WARNING time value is in micro_seconds
  if (duration == 0)                                      //timeout event
  {
    resetFlags();
    digitalWrite(ledPin, ledState);
  }
  if (duration <= 2200)
  {
    //bit 0 received
    bitstream[counter] = 0;
    counter ++;
    PPD_flag = 0;
  } else if (duration <= 5200)
  {
    //bit 1 received
    bitstream[counter] = 1;
    counter++;
    PPD_flag = 0;
  } else
  {
    //bit POINTER received
    if (PPD_flag)                           //if the previouse pulse was a POINTER
    {
      PPDmillisec = millis();
      if (counter == 89) dataFlag = 1;      //if all the 89 bit are reiceved
      else
        resetFlags();
      counter = 0;
    }
    PPD_flag = 1;
  }
  if (counter == 24 && dataFlag)            //conversion of hours/minutes/seconds
  {
    Seconds = bitstream[0] + bitstream[1] * 2 + bitstream[2] * 4 + bitstream[3] * 8 + bitstream[5] * 10 + bitstream[6] * 20 + bitstream[7] * 40;
    Minutes = bitstream[8] + bitstream[9] * 2 + bitstream[10] * 4 + bitstream[11] * 8 + bitstream[13] * 10 + bitstream[14] * 20 + bitstream[15] * 40;
    Hours = bitstream[17] + bitstream[18] * 2 + bitstream[19] * 4 + bitstream[20] * 8 + bitstream[22] * 10 + bitstream[23] * 20;
    timeFlag = 1;
  }
  if (counter == 37 && dataFlag)            //conversion of days of the year/year
  {
    Days_old = DaysOY;                     //store the previous day of the year
    DaysOY = bitstream[26] + bitstream[27] * 2 + bitstream[28] * 4 + bitstream[29] * 8 + bitstream[31] * 10 + bitstream[32] * 20 + bitstream[33] * 40 + bitstream[34] * 80 + bitstream[35] * 100 + bitstream[36] * 200;
    Year = 2000 + bitstream[44] + bitstream[45] * 2 + bitstream[46] * 4 + bitstream[47] * 8 + bitstream[49] * 10 + bitstream[50] * 20 + bitstream[51] * 40 + bitstream[52] * 80;
    //check for leap yeara
    if (!serialFlag)
    {
      if (Year % 4  == 0)
      {
        if (Year % 100 != 0)
          daysInMonth[1] = 29;
        else if (Year % 400 == 0)
          daysInMonth[1] = 29;
      }
    }
    yearFlag = 1;
  }
  if (counter == 38 && dataFlag)          //conversion of days/month
  {
    if (DaysOY != Days_old)              //if the day of the year is different check the month
    {
      monthFlag = 0;
    }
    if (!monthFlag)                       //calculation for the month and the day
    {
      int i = 0;
      Days = DaysOY;
      while ( Days > daysInMonth[i])
      {
        Days -= daysInMonth[i];
        i++;
      }
      Month = i + 1;
      monthFlag = 1;
    }
    serialFlag = (yearFlag && timeFlag);
  }
  if (counter == 39 && serialFlag)      //sending on the serial port
  {
    char buf[40];                       //buffer string
    char strg[32];                      //string
    sprintf(buf, "$");
    unsigned int interval = millis() - PPDmillisec;
    interval = round(interval / 10.0);
    //string format $GPZDAhhmmss.ss,xx,xx,yyyy,mm,dd,*CH<CR><LF>
    sprintf(strg, "GPZDA%02d%02d%02d.%02d,%02d,%02d,%04d,%02d,%02d", Hours, Minutes, Seconds, interval, TMZh, TMZh , Year, Month, Days);
    //String CHECKSUM
    int XOR;
    XOR = getCheckSum(strg, 32);
    char CHK[5];
    sprintf(CHK, "*%2X\r\n", XOR);
    //building the buffer string
    strcat(buf, strg);
    strcat(buf, CHK);
    //send the buffer to the serial port
    Serial.print(buf);
    //Serial.print("SBS:\t"); Serial.println(SBS+1);
  }
  //if (counter == 43)
  //    SBSfunction();
  if (counter > 89)
  {
    resetFlags();
  }
  //led is switched on to indicate trasmission on the serial port
  if (ledState != serialFlag)
  {
    ledState = serialFlag;
    digitalWrite(ledPin, ledState);
  }
}
/* CHECKSUM FUNCTION */
uint8_t getCheckSum(char *string, int buflen)
{
  int XOR = string[0];
  for (int i = 1; i < buflen; i++)
  {
    XOR = XOR ^ string[i];
  }
  return XOR;
}
/* RESET FLAGS FUNCTION */
void resetFlags (void)
{
  counter = 0;
  dataFlag = 0;
  timeFlag = 0;
  yearFlag = 0;
  serialFlag = 0;
  ledState = 0;
}
/* SBS FUNCTION */
//void SBSfunction (void) {
//  for (int i = 0; i < 17; i++)
//  {
//    SBS += bitstream[87 - i];
//    SBS << 1;
//  }
//  SBS += 1;
//}

Why is an Arduino even necessary for this ?

buy it http://orcatechnologies.com/gs-101/

ieee488:
Why is an Arduino even necessary for this ?

buy it GS-101B Data Sheet - ORCA Technologies

Nice suggestion.
Although that wouldn't improve my coding abilities much and lower quite a bit my small founds.

Does that code work?

Pete

Yes, the code works as it is. I reiceve the NMEA string if the arduino has reiceved all the bits.

Which Arduino are you using?

Yes, the code works as it is

I don't think so.

What is this supposed to do?

    resetFlags;

Also, you should follow the code to figure out what happens when duration is zero.

A zero is apparently a 20ms pulse but your test for a zero is 2200. Why?
[edit]note that the first message originally said:
80ms POINTER
50ms HIGH
20ms LOW
and thus my question.[/edit]

You declare duration as unsigned. Unless you are using a Due or Zero, this means it is 16 bits in which case it can't handle a pulse length of 50ms.

Pete

The code send on the serial the correct string when the sync (double long pulse) and 100 bits are reiceved.
It doesn't send out any string if <100 are reiceved.

The function "resetFlags" zeros all the boolean flags. It's at the end of the code.

/* RESET FLAGS FUNCTION */
void resetFlags (void)
{
  counter = 0;
  dataFlag = 0;
  timeFlag = 0;
  yearFlag = 0;
  serialFlag = 0;
  ledState = 0;
}

My error on the pulse duration. 100bps with bit time of 10ms.
I have 3 type of square impulses with different duration:
8ms POINTER
5ms HIGH
2ms LOW
There is a small jitter in the pulses (sometimes 2.1ms) so I use 2200micro_sec to catch them correctly.

You declare duration as unsigned. Unless you are using a Due or Zero, this means it is 16 bits in which case it can't handle a pulse length of 50ms.

Corrected the declaration: duration
This code is the traslation of the original (which is in italian). In the original both duration and PPSmillisec are unsigned long (as it's now after the correction).

Thanks for the help

The function "resetFlags" zeros all the boolean flags

I didn't ask you what the resetFlags function does. I asked you what this statement is supposed to do:

resetFlags;

Hint: it doesn't call the resetFlags function.

Pete

ok...Added parentesys.
Thanks