Looking at an Example RDS Stream

A typical RDS data stream

An example is needed as to how to assemble a RDS data stream. The assemble goes into a ring buffer that is continuously broadcast by the transmitter.

A RDS data stream

Once the data set of 64 groups has been decided we can generate the bytes(bits) for each group. Here the first sector is translated in to functions. Every one of the 32 groups needs a function that generates 13 bytes that are added on to the data set. Some groups like that used for Rich Text or the clock can change relatively frequently, so knowing where these groups are in the data set only those group sites need to be renewed. Of cause the complete data set still needs to be re-compiled into symbols before transmission.


ConstructFrame0A((byte)0, (byte)0);
ConstructFrame2A((byte)1, (byte)0);
ConstructFrame10A((byte)2, (byte)0);    
ConstructFrame1A((byte)3, _SLC.slc, (word)_PIN.pin, (byte)_RPC);        
ConstructFrame0A((byte)4, (byte)1);
ConstructFrame2A((byte)5, (byte)1);
ConstructFrame15B((byte)6, (byte)0);
ConstructFrame15B((byte)7, (byte)1);    
ConstructFrame0A((byte)8, (byte)2);
ConstructFrame2A((byte)9, (byte)2);
ConstructFrame1A((byte)10, _SLC.slc, (word)_PIN.pin, (byte)_RPC);        
ConstructFrame15B((byte)11, (byte)2);    
ConstructFrame0A((byte)12, (byte)3);
ConstructFrame2A((byte)13, (byte)3);
ConstructFrame10A((byte)14, (byte)1);            
ConstructFrame15B((byte)15, (byte)3);

The functions take the data set location (0-63) where the group is to be inserted, the sequence number of the group where the data spans several group transmissions and specific group data if required.

Examining one of the functions shows how the group is generated and cascaded into the data set. In this case the RichText group 2A. The text is sent 4 characters per group, so a full 64 bytetext requires a sequence of 16 group 2A's.

//  A frame is 104bits, 4 x26bit blocks
//  Block A payload 00:15
//  Block A crc     16:25
//  Block B payload 26:41
//  Block B crc     42:51
//  Block C payload 52:67
//  Block C crc     68:77
//  Block D payload 78:93
//  Block D crc     94:103

void ConstructFrame2A(byte frame_number, byte sequence) 
{
//  This constructs a 2A frame is a sequence of up to 16 = 64 chars
//  Here we just take 4 chars from the RT_list. The maximum is 64 chars 
//  or 16 different frames
//  Note: <64 char text is to be terminated with <cr> and screen jumps can be made with <lf>
//  
  struct rds_frame f;
  char a0 = 0;
  char a1 = 0;
  char a2 = 0;
  char a3 = 0;
  word b = 0x0000;
  word c = 0x0000;
  word d = 0x0000;
  //  The characters go out in groups of 4 so
  int sequence_position = (sequence * 4);
  if(sequence > 15) sequence = 0;  //  The limit is 0xF
  //  Make block B
  b = GROUP_2A;
  if(tp) b |= TP_ON;
  //  else it is already 0
  b |= _PTY.pty;  //  Add on PTY
  //  Add on A/B flag and sequence number (4 bits)
  if(ab) b |= AB_ON;
  //  else it is already 0
  b |= ((word)sequence & 0x000F);
  //  Make blocks C and D together
  a0 = RT_list[sequence_position];
  a1 = RT_list[sequence_position + 1];
  a2 = RT_list[sequence_position + 2];
  a3 = RT_list[sequence_position + 3];
  if(a0 < 0x0A) a0 = 0x20;
  else if (a0 > 0x7F) a0 = 0x2A;
  if(a1 < 0x0A) a1 = 0x20;
  else if (a1 > 0x7F) a1 = 0x2A;
  if(a2 < 0x0A) a2 = 0x20;
  else if (a2 > 0x7F) a2 = 0x2A;
  if(a3 < 0x0A) a3 = 0x20;
  else if (a3 > 0x7F) a3 = 0x2A;
  c |= (((word)a0 << 8) & 0xFF00);
  c |= ((word)a1 & 0x00FF);
  d |= (((word)a2 << 8) & 0xFF00);
  d |= ((word)a3 & 0x00FF);
  //  Now make the complete frame
  f = MakeFrame(_PI.pi, b, c, d, TRUE);
  for(d = 0; d < 13; d++) payload.part.frame[frame_number].frame[d] = f.frame[d];
  return;
}

struct rds_frame MakeFrame( word payload_a, word payload_b, word payload_c, word payload_d, bool bType) 
{
//  Makes a frame given all the payloads, also generates the crc for each payload
  int ii = 0;
  struct rds_frame f;
  SetblockA(&f, payload_a);
  SetblockB(&f, payload_b);
  SetblockC(&f, payload_c, bType);
  SetblockD(&f, payload_d);
  return f;
}

First of all the 4 payloads (A, B, C, D) of the group are defined.

Then the group is assembled by combining the 4 data payloads with the CRC that goes with each payload.

Here is the detail of the block C calculation of a group. the other blocks A, B, D are calculated in a similar way, using the offset defined for each block.


void SetblockC(struct rds_frame* f, word payload, bool bAtype) 
{
//  Replaces the payload of block C in a frame. Generates the crc and offset = 0x0168;
//  The checksum is the lower 10 bits of crc
  word check = 0;
  check = crc(payload);
  if(bAtype) check = (check^0x0168); //  offset C
  else  check = (check^0x0350); //  offset C'
  f->frame[6] &= 0xF0;
  f->frame[6] |= (byte)((payload >> 12) & 0x000F);
  f->frame[7] = (byte)((payload >> 4) & 0x00FF);
  f->frame[8] = (byte)((payload << 4) & 0x00F0);
  f->frame[8] |= (byte)((check >> 6) & 0x000F);
  f->frame[9] &= 0x03; //  Clear upper 6 bits
  f->frame[9] |= (byte)((check <<2) & 0x00FC);  
}

The auxilary functions are given here:


word crc(word w) 
{
//  Calculates the 10 bits in a word equivalent to the checksum of w, without the offset
//  This the xor sum of the bit_code for each bit set in w
word c = 0x00;
word t = 0x00;
byte i = 0;
  if((w & 0x0001) == 0x0001) c = c^bit_code[0];
  if((w & 0x0002) == 0x0002) c = c^bit_code[1];
  if((w & 0x0004) == 0x0004) c = c^bit_code[2];
  if((w & 0x0008) == 0x0008) c = c^bit_code[3];
  if((w & 0x0010) == 0x0010) c = c^bit_code[4];
  if((w & 0x0020) == 0x0020) c = c^bit_code[5];
  if((w & 0x0040) == 0x0040) c = c^bit_code[6];
  if((w & 0x0080) == 0x0080) c = c^bit_code[7];
  if((w & 0x0100) == 0x0100) c = c^bit_code[8];
  if((w & 0x0200) == 0x0200) c = c^bit_code[9];
  if((w & 0x0400) == 0x0400) c = c^bit_code[10];
  if((w & 0x0800) == 0x0800) c = c^bit_code[11];
  if((w & 0x1000) == 0x1000) c = c^bit_code[12];
  if((w & 0x2000) == 0x2000) c = c^bit_code[13];
  if((w & 0x4000) == 0x4000) c = c^bit_code[14];
  if((w & 0x8000) == 0x8000) c = c^bit_code[15];
  return c;
}

struct rds_frame {
//  A RDS frame has 104 bits = 13 bytes consisting of 4 blocks
//  Manipulation of the frame is done by functions
  byte frame[13];
};

//  The payload is up to 64 frames = 16 * 13 bytes = 832 bytes
#define PAYLOAD_SIZE 832
#define DATA_LENGTH 1664
#define DATA_SET_SIZE 1664
#define HALF_DATA_SET_SIZE  832
#define SYMBOL_SIZE 288

union rds_payload {
  byte frame_byte[PAYLOAD_SIZE];
  struct {
    struct rds_frame frame[64];
    byte frame_count; //  The number of frames in a data set. Maximum 64
  } part;
};