1 /*
2  * Copyright (C) 2014 Bastian Bloessl <bloessl@ccs-labs.org>
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #define dout debug && std::cout
19 #define lout log && std::cout
20 
21 #include "decoder_impl.h"
22 #include "constants.h"
23 #include <gnuradio/io_signature.h>
24 
25 using namespace gr::rds;
26 
27 decoder::sptr
make(bool log,bool debug)28 decoder::make(bool log, bool debug) {
29   return gnuradio::get_initial_sptr(new decoder_impl(log, debug));
30 }
31 
decoder_impl(bool log,bool debug)32 decoder_impl::decoder_impl(bool log, bool debug)
33 	: gr::sync_block ("gr_rds_decoder",
34 			gr::io_signature::make (1, 1, sizeof(char)),
35 			gr::io_signature::make (0, 0, 0)),
36 	log(log),
37 	debug(debug)
38 {
39 	set_output_multiple(104);  // 1 RDS datagroup = 104 bits
40 	message_port_register_out(pmt::mp("out"));
41 	enter_no_sync();
42 }
43 
~decoder_impl()44 decoder_impl::~decoder_impl() {
45 }
46 
47 
48 ////////////////////////// HELPER FUNCTIONS /////////////////////////
49 
enter_no_sync()50 void decoder_impl::enter_no_sync() {
51 	presync = false;
52 	d_state = NO_SYNC;
53 }
54 
enter_sync(unsigned int sync_block_number)55 void decoder_impl::enter_sync(unsigned int sync_block_number) {
56 	wrong_blocks_counter   = 0;
57 	blocks_counter         = 0;
58 	block_bit_counter      = 0;
59 	block_number           = (sync_block_number + 1) % 4;
60 	group_assembly_started = false;
61 	d_state                = SYNC;
62 }
63 
64 /* see Annex B, page 64 of the standard */
calc_syndrome(unsigned long message,unsigned char mlen)65 unsigned int decoder_impl::calc_syndrome(unsigned long message,
66 		unsigned char mlen) {
67 	unsigned long reg = 0;
68 	unsigned int i;
69 	const unsigned long poly = 0x5B9;
70 	const unsigned char plen = 10;
71 
72 	for (i = mlen; i > 0; i--)  {
73 		reg = (reg << 1) | ((message >> (i-1)) & 0x01);
74 		if (reg & (1 << plen)) reg = reg ^ poly;
75 	}
76 	for (i = plen; i > 0; i--) {
77 		reg = reg << 1;
78 		if (reg & (1<<plen)) reg = reg ^ poly;
79 	}
80 	return (reg & ((1<<plen)-1));	// select the bottom plen bits of reg
81 }
82 
decode_group(unsigned int * group)83 void decoder_impl::decode_group(unsigned int *group) {
84 	// raw data bytes, as received from RDS.
85 	// 8 info bytes, followed by 4 RDS offset chars: ABCD/ABcD/EEEE (in US)
86 	unsigned char bytes[12];
87 
88 	// RDS information words
89 	bytes[0] = (group[0] >> 8U) & 0xffU;
90 	bytes[1] = (group[0]      ) & 0xffU;
91 	bytes[2] = (group[1] >> 8U) & 0xffU;
92 	bytes[3] = (group[1]      ) & 0xffU;
93 	bytes[4] = (group[2] >> 8U) & 0xffU;
94 	bytes[5] = (group[2]      ) & 0xffU;
95 	bytes[6] = (group[3] >> 8U) & 0xffU;
96 	bytes[7] = (group[3]      ) & 0xffU;
97 
98 	// RDS offset words
99 	bytes[8] = offset_chars[0];
100 	bytes[9] = offset_chars[1];
101 	bytes[10] = offset_chars[2];
102 	bytes[11] = offset_chars[3];
103 
104 	pmt::pmt_t data(pmt::make_blob(bytes, 12));
105 	pmt::pmt_t meta(pmt::PMT_NIL);
106 
107 	pmt::pmt_t pdu(pmt::cons(meta, data));  // make PDU: (metadata, data) pair
108 	message_port_pub(pmt::mp("out"), pdu);
109 }
110 
work(int noutput_items,gr_vector_const_void_star & input_items,gr_vector_void_star & output_items)111 int decoder_impl::work (int noutput_items,
112 		gr_vector_const_void_star &input_items,
113 		gr_vector_void_star &output_items)
114 {
115 	const bool *in = (const bool *) input_items[0];
116 	(void) output_items;
117 
118 	dout << "RDS data decoder at work: input_items = "
119 		<< noutput_items << ", /104 = "
120 		<< noutput_items / 104 << std::endl;
121 
122 	int i=0,j;
123 	unsigned long bit_distance, block_distance;
124 	unsigned int block_calculated_crc, block_received_crc, checkword,dataword;
125 	unsigned int reg_syndrome;
126 	unsigned char offset_char('x');  // x = error while decoding the word offset
127 
128 /* the synchronization process is described in Annex C, page 66 of the standard */
129 	while (i<noutput_items) {
130 		reg=(reg<<1)|in[i];		// reg contains the last 26 rds bits
131 		switch (d_state) {
132 			case NO_SYNC:
133 				reg_syndrome = calc_syndrome(reg,26);
134 				for (j=0;j<5;j++) {
135 					if (reg_syndrome==syndrome[j]) {
136 						if (!presync) {
137 							lastseen_offset=j;
138 							lastseen_offset_counter=bit_counter;
139 							presync=true;
140 						}
141 						else {
142 							bit_distance=bit_counter-lastseen_offset_counter;
143 							if (offset_pos[lastseen_offset]>=offset_pos[j])
144 								block_distance=offset_pos[j]+4-offset_pos[lastseen_offset];
145 							else
146 								block_distance=offset_pos[j]-offset_pos[lastseen_offset];
147 							if ((block_distance*26)!=bit_distance) presync=false;
148 							else {
149 								lout << "@@@@@ Sync State Detected" << std::endl;
150 								enter_sync(j);
151 							}
152 						}
153 					break; //syndrome found, no more cycles
154 					}
155 				}
156 			break;
157 			case SYNC:
158 /* wait until 26 bits enter the buffer */
159 				if (block_bit_counter<25) block_bit_counter++;
160 				else {
161 					good_block=false;
162 					dataword=(reg>>10) & 0xffff;
163 					block_calculated_crc=calc_syndrome(dataword,16);
164 					checkword=reg & 0x3ff;
165 /* manage special case of C or C' offset word */
166 					if (block_number==2) {
167 						block_received_crc=checkword^offset_word[block_number];
168 						if (block_received_crc==block_calculated_crc) {
169 							good_block=true;
170 							offset_char = 'C';
171 						} else {
172 							block_received_crc=checkword^offset_word[4];
173 							if (block_received_crc==block_calculated_crc) {
174 								good_block=true;
175 								offset_char = 'c';  // C' (C-Tag)
176 							} else {
177 								wrong_blocks_counter++;
178 								good_block=false;
179 							}
180 						}
181 					}
182 					else {
183 						block_received_crc=checkword^offset_word[block_number];
184 						if (block_received_crc==block_calculated_crc) {
185 							good_block=true;
186 							if (block_number==0) offset_char = 'A';
187 							else if (block_number==1) offset_char = 'B';
188 							else if (block_number==3) offset_char = 'D';
189 						} else {
190 							wrong_blocks_counter++;
191 							good_block=false;
192 						}
193 					}
194 /* done checking CRC */
195 					if (block_number==0 && good_block) {
196 						group_assembly_started=true;
197 						group_good_blocks_counter=1;
198 					}
199 					if (group_assembly_started) {
200 						if (!good_block) group_assembly_started=false;
201 						else {
202 							group[block_number]=dataword;
203 							offset_chars[block_number] = offset_char;
204 							group_good_blocks_counter++;
205 						}
206 						if (group_good_blocks_counter==5) decode_group(group);
207 					}
208 					block_bit_counter=0;
209 					block_number=(block_number+1) % 4;
210 					blocks_counter++;
211 /* 1187.5 bps / 104 bits = 11.4 groups/sec, or 45.7 blocks/sec */
212 					if (blocks_counter==50) {
213 						if (wrong_blocks_counter>35) {
214 							lout << "@@@@@ Lost Sync (Got " << wrong_blocks_counter
215 								<< " bad blocks on " << blocks_counter
216 								<< " total)" << std::endl;
217 							enter_no_sync();
218 						} else {
219 							lout << "@@@@@ Still Sync-ed (Got " << wrong_blocks_counter
220 								<< " bad blocks on " << blocks_counter
221 								<< " total)" << std::endl;
222 						}
223 						blocks_counter=0;
224 						wrong_blocks_counter=0;
225 					}
226 				}
227 			break;
228 			default:
229 				d_state=NO_SYNC;
230 			break;
231 		}
232 		i++;
233 		bit_counter++;
234 	}
235 	return noutput_items;
236 }
237