]> git.kernelconcepts.de Git - rdstmc.git/blob - decoder/rds.c
Added
[rdstmc.git] / decoder / rds.c
1 #include <stdio.h>
2 #include <string.h>
3 #include <stdlib.h>
4 #include <time.h>
5 #include <unistd.h>
6 #include <sys/stat.h>
7 #include <fcntl.h>
8 #include <limits.h>
9
10 #include <sqlite3.h>
11
12 #include "rds.h"
13 #include "rds_consts.h"
14 #include "tmc.h"
15
16 //#define DEBUG 1
17
18
19 static struct rds_info_s rds_info;
20 static struct tm rds_time;
21
22 static struct _RDS_Private {
23         void (*rds_PI_cb)(unsigned short PI, unsigned char ccode, unsigned char ptype, unsigned char pref, void *user_data);
24         void *rds_PI_cb_data;
25         void (*rds_sname_cb)(char *sname, void *user_data);
26         void *rds_sname_cb_data;
27         void (*rds_sinfo_cb)(unsigned char TP, unsigned char TA, unsigned char MS, unsigned char PTY, void *user_data);
28         void *rds_sinfo_cb_data;
29         void (*rds_radiotext_cb)(char *radio_text, void *user_data);
30         void *rds_radiotext_cb_data;
31         void (*rds_date_time_cb)(struct tm *dtime, void *user_data);
32         void *rds_date_time_cb_data;
33         void (*rds_afinfo_cb)(unsigned char cnt, double *AF, void *user_data);
34         void *rds_afinfo_cb_data;
35 } _rds_private;
36
37 void rds_init(void) {
38         memset(&_rds_private, 0, sizeof(struct _RDS_Private));
39 }
40
41 void rds_set_PI_cb(void (*rds_PI_cb)(unsigned short PI, unsigned char ccode, unsigned char ptype, unsigned char pref, void *user_data), void *user_data)
42 {
43         _rds_private.rds_PI_cb = rds_PI_cb;
44         _rds_private.rds_PI_cb_data = user_data;
45 }
46
47 void rds_set_sname_cb(void (*rds_sname_cb)(char *sname, void *user_data), void *user_data)
48 {
49         _rds_private.rds_sname_cb = rds_sname_cb;
50         _rds_private.rds_sname_cb_data = user_data;
51 }
52
53 void rds_set_sinfo_cb(void (*rds_sinfo_cb)(unsigned char TP, unsigned char TA, unsigned char MS, unsigned char PTY, void *user_data), void *user_data)
54 {
55         _rds_private.rds_sinfo_cb = rds_sinfo_cb;
56         _rds_private.rds_sinfo_cb_data = user_data;
57 }
58
59 void rds_set_afinfo_cb(void (*rds_afinfo_cb)(unsigned char cnt, double *AF, void *user_data), void *user_data)
60 {
61         _rds_private.rds_afinfo_cb = rds_afinfo_cb;
62         _rds_private.rds_afinfo_cb_data = user_data;
63 }
64
65 void rds_set_radiotext_cb(void (*rds_radiotext_cb)(char *radio_text, void *user_data), void *user_data)
66 {
67         _rds_private.rds_radiotext_cb = rds_radiotext_cb;
68         _rds_private.rds_radiotext_cb_data = user_data;
69 }
70
71 void rds_set_date_time_cb(void (*rds_date_time_cb)(struct tm *dtime, void *user_data), void *user_data)
72 {
73         _rds_private.rds_date_time_cb = rds_date_time_cb;
74         _rds_private.rds_date_time_cb_data = user_data;
75 }
76
77
78 int rds_receive_group(int rds_fd, unsigned short *rdsgroup)
79 {
80 static unsigned char expected = 0;
81 int rb;
82 unsigned char rbuf[3];
83 unsigned short block;
84 unsigned char offset;
85
86         rb = read(rds_fd, rbuf, 3);
87         if (rb <= 0)
88                 exit(0); // just for testing
89         if (rb != 3)
90                 printf("#read err rb=%d\n", rb);
91         block = rbuf[0] | (rbuf[1] << 8);
92         offset = rbuf[2] & 0x07;
93
94         if (rbuf[2] & 0x80) {
95                 if (OutputFlags & RDS_RECEIVE_INDICATOR)
96                         printf("E");
97         } else {
98                 // printf("group block=0x%04x offs=0x%02x\n", block, offset);
99                 if (offset != expected) {
100                         if (OutputFlags & RDS_RECEIVE_INDICATOR)
101                                 printf("block out of sync - resetting\n");
102                         expected = 0;
103                         memset(rdsgroup, 0, sizeof(rdsgroup));
104                 } else {
105                         rdsgroup[offset] = block;
106                         expected++;
107                         if (expected >= 4) {
108                                 expected = 0;
109                                 if (OutputFlags & RDS_RECEIVE_INDICATOR)
110                                         printf(".\n");
111                                 return 1;
112                         }
113                 }
114         }
115         if (OutputFlags & RDS_RECEIVE_INDICATOR)
116                 printf(".");
117         return 0;
118 }
119
120
121 enum RDSGroupType { GROUP_0A=0, GROUP_0B, GROUP_1A, GROUP_1B, GROUP_2A, GROUP_2B, 
122                     GROUP_3A, GROUP_3B, GROUP_4A, GROUP_4B, GROUP_5A, GROUP_5B, 
123                     GROUP_6A, GROUP_6B, GROUP_7A, GROUP_7B, GROUP_8A, GROUP_8B, 
124                     GROUP_9A, GROUP_9B, GROUP_10A, GROUP_10B, GROUP_11A, GROUP_11B, 
125                     GROUP_12A, GROUP_12B, GROUP_13A, GROUP_13B, GROUP_14A, GROUP_14B, 
126                     GROUP_15A, GROUP_15B, GROUP_UNKNOWN };
127
128 void rds_decode_group(unsigned short *rdsgroup)
129 {
130 static unsigned short ogrp[4];
131 unsigned char grp_type = (rdsgroup[1] >> 11);
132 unsigned char offs;
133 static unsigned char otextAB = 0, newtext = 0;
134 unsigned short PI = rdsgroup[0];
135 unsigned char textAB = 0;
136 int local_time_off = 0;
137 int day_code = 0;
138 int year_, mon_, K;
139
140         if (ogrp[0] == rdsgroup[0] && ogrp[1] == rdsgroup[1] && ogrp[2] == rdsgroup[2] && ogrp[3] == rdsgroup[3])
141                 return;
142         else {
143                 ogrp[0] = rdsgroup[0];
144                 ogrp[1] = rdsgroup[1];
145                 ogrp[2] = rdsgroup[2];
146                 ogrp[3] = rdsgroup[3];
147                 //printf("grp: 0x%04x 0x%04x 0x%04x 0x%04x\n", rdsgroup[0], rdsgroup[1], rdsgroup[2], rdsgroup[3]);
148         }
149         if (rds_info.PI != PI) {
150                 /* station change? */
151                 memset(&rds_info, 0, sizeof(rds_info));
152                 rds_info.LTN = -1;
153                 memset(&rds_time, 0, sizeof(rds_time));
154                 rds_info.ccode = (PI & 0xf000) >> 12;
155                 rds_info.ptype = (PI & 0x0f00) >> 8;
156                 rds_info.pref = (PI & 0x00ff);
157                 if (rds_info.pref == 0) /* something is wrong here */
158                         return;
159                 rds_info.PI = rdsgroup[0];
160                 if (_rds_private.rds_PI_cb != NULL)
161                         _rds_private.rds_PI_cb(rds_info.PI, rds_info.ccode, rds_info.ptype, rds_info.pref, _rds_private.rds_PI_cb_data);
162                 if (OutputFlags & RDS_OUTPUT_RDSINFO)
163                         printf("PI=%d ccode=%X ptype=%X '%s' '%s' pref=%d\n", rds_info.PI, rds_info.ccode, rds_info.ptype, ptype_stext[rds_info.ptype], ptype_ltext[rds_info.ptype], rds_info.pref);
164         }
165
166         switch (grp_type) {
167                 case GROUP_0A: { /* basic switching and tuning */
168                         unsigned char AFc1, AFc2;
169                         float AF1=0, AF2=0;
170
171                         offs = (rdsgroup[1] & 0x03);
172                         rds_info.sname[offs*2] = ((rdsgroup[3] & 0xff00) >> 8);
173                         rds_info.sname[(offs*2)+1] = rdsgroup[3] & 0x00ff;
174                         if (_rds_private.rds_sname_cb != NULL)
175                                 _rds_private.rds_sname_cb(rds_info.sname, _rds_private.rds_sname_cb_data);
176
177                         rds_info.TA = (rdsgroup[1] & 0x10) >> 4;
178                         rds_info.TP = (rdsgroup[1] & 0x400) >> 10;
179                         rds_info.MS = (rdsgroup[1] & 0x08) >> 3;
180                         rds_info.PTY = (rdsgroup[1] & 0x3e0) >> 5;
181                         if (_rds_private.rds_sinfo_cb != NULL)
182                                 _rds_private.rds_sinfo_cb(rds_info.TP, rds_info.TA, rds_info.MS, rds_info.PTY, _rds_private.rds_sinfo_cb_data);
183
184                         AFc1 = ((rdsgroup[2] & 0xff00) >> 8);
185                         AFc2 = (rdsgroup[2] & 0x00ff);
186                         if (AFc1 > 225 && AFc1 < 250) {
187                                 if (OutputFlags & RDS_OUTPUT_STATION_ID)
188                                         printf(" %d AFs follow:\n", AFc1-224);
189                         } else if (AFc1 > 0 && AFc1 < 205)
190                                 AF1 = (float)87.5 + ((float)AFc1 * .1);
191                         else if (AFc1 == 205)
192                                 printf(" filler- \n");
193                         if (AFc2 > 0 && AFc2 < 205)
194                                 AF2 = (float)87.5 + ((float)AFc2 * .1);
195
196                         if (OutputFlags & RDS_OUTPUT_STATION_ID)
197                                 printf("sname = '%s' %s %s %s AF1=%3.2f AF2=%3.2f PTY='%s'\n",  rds_info.sname, rds_info.TP ? "TP" : "", rds_info.TA ? "TA" : "", rds_info.MS ? "M" : "S", AF1, AF2, PTY_text[rds_info.PTY]);
198                         }
199                         break;
200                 case GROUP_0B:
201                         if (OutputFlags & RDS_OUTPUT_UNKNGRP)
202                                 printf("group 0B\n");
203                         break;
204                 case GROUP_1A: { /* Programme Item Number and slow labelling codes */
205                         unsigned char variant, day, hour, minute;
206                         unsigned short tmc_id;
207
208                         variant = (rdsgroup[2] & (0x07 << 12)) >> 12;
209                         day = (rdsgroup[3] & (0x1f << 11)) >> 11;
210                         hour = (rdsgroup[3] & (0x1f << 6)) >> 6;
211                         minute = (rdsgroup[3] & 0x3f);
212                         if (OutputFlags & RDS_OUTPUT_UNKNGRP)
213                                 printf("1A var=%d day=%d %d:%d ", variant, day, hour, minute);
214                         if (variant == 0) {
215                                 rds_info.ECC = (rdsgroup[2] & 0xff);
216                                 printf(" ccode=%d ECC=0x%02x '%s' ", rds_info.ccode, rds_info.ECC, ECC_text[((rds_info.ECC & 0x0f)*16)+(rds_info.ccode-1)]);
217                         }
218                         if (variant == 1) {
219                                 tmc_id = (rdsgroup[2] & 0x0fff);
220                                 printf(" TMC ID=0x%04x", tmc_id);
221                         }
222                         if (variant == 2) {
223                                 printf(" Paging ID=0x%04x", (rdsgroup[2] & 0x0fff));
224                         }
225                         if (variant == 3) {
226                                 rds_info.lang_code = (rdsgroup[2] & 0xff);
227                                 printf(" language=0x%02x ", rds_info.lang_code);
228                                 switch (rds_info.lang_code) {
229                                         case 0x03:
230                                                 printf("Catalan ");
231                                                 break;
232                                         case 0x08:
233                                                 printf("German ");
234                                                 break;
235                                         case 0x0a:
236                                                 printf("Spanish ");
237                                                 break;
238                                         case 0x15:
239                                                 printf("Italian ");
240                                                 break;
241                                         case 0x18:
242                                                 printf("Latvian ");
243                                                 break;
244                                         case 0x1d:
245                                                 printf("Dutch ");
246                                                 break;
247                                         case 0x27:
248                                                 printf("Finnish ");
249                                                 break;
250                                         default:
251                                                 printf("unknown ");
252                                                 break;
253                                 };
254                         }
255                         if (variant == 7) {
256                                 printf(" EWS Channel ID=0x%04x", (rdsgroup[2] & 0x0fff));
257                         }
258                         printf("\n");
259                         } break;
260                 case GROUP_1B:
261                         if (OutputFlags & RDS_OUTPUT_UNKNGRP)
262                                 printf("group 1B\n");
263                         break;
264                 case GROUP_2A: /* 2A has 64 chars, 4 per group */
265                 case GROUP_2B: /* 2B has 32 chars, 2 per group */
266                         offs = (rdsgroup[1] & 0x0f);
267                         textAB = (rdsgroup[1] & 0x10);
268                         if (textAB != otextAB) {
269                                 memset(rds_info.rtext, ' ', 64);
270                                 rds_info.rtext[64] = '\0';
271                         }
272                         otextAB = textAB;
273                         if (grp_type == GROUP_2A) {
274                                 rds_info.rtext[offs*4] = ((rdsgroup[2] & 0xff00) >> 8);
275                                 rds_info.rtext[(offs*4)+1] = (rdsgroup[2] & 0x00ff);
276                                 rds_info.rtext[(offs*4)+2] = ((rdsgroup[3] & 0xff00) >> 8);
277                                 rds_info.rtext[(offs*4)+3] = (rdsgroup[3] & 0x00ff);
278                         } else {
279                                 rds_info.rtext[offs*2] = ((rdsgroup[3] & 0xff00) >> 8);
280                                 rds_info.rtext[(offs*2)+1] = (rdsgroup[3] & 0x00ff);
281                         }
282                         if (offs == 0) /* new text always starts at 0 */
283                                 newtext = 1;
284                         if ((offs == 15)) { /* all parts received */
285                                 if (newtext == 1) {
286                                         if (_rds_private.rds_radiotext_cb != NULL)
287                                                 _rds_private.rds_radiotext_cb(rds_info.rtext, _rds_private.rds_radiotext_cb_data);
288                                         if (OutputFlags & RDS_OUTPUT_RADIO_TEXT)
289                                                 printf("RT '%s'\n", rds_info.rtext);
290                                         newtext = 0;
291                                 }
292                         }
293                         break;
294                 case GROUP_3A: /* ODA - TMC alarm, */
295                         rds_info.AID = rdsgroup[3];
296                         /* TMC AID */
297                         if (rds_info.AID == 0xcd46 || rds_info.AID == 0x0d45) {
298                                 if ((rdsgroup[2] & 0xc000) == 0) {
299                                         rds_info.LTN = (rdsgroup[2] & 0x0fc0) >> 6;
300                                         rds_info.AFI = (rdsgroup[2] & 0x0020) >> 5;
301                                         rds_info.M = (rdsgroup[2] & 0x0010) >> 4;
302                                         rds_info.I = (rdsgroup[2] & 0x0008) >> 3;
303                                         rds_info.N = (rdsgroup[2] & 0x0004) >> 2;
304                                         rds_info.R = (rdsgroup[2] & 0x0002) >> 1;
305                                         rds_info.U = (rdsgroup[2] & 0x0001);
306                                 }
307                                 if (rdsgroup[2] & 0x4000) {
308                                         /* SID */
309                                         if (rds_info.M) {
310                                                 rds_info.G = (rdsgroup[2] & 0x3000) >> 12;
311                                                 rds_info.Ta = (rdsgroup[2] & 0x0030) >> 4;
312                                                 rds_info.Tw = (rdsgroup[2] & 0x000c) >> 2;
313                                                 rds_info.Td = (rdsgroup[2] & 0x0003);
314                                         } else {
315                                         }
316                                 }
317                                 if (OutputFlags & RDS_OUTPUT_RDSINFO) {
318                                         printf("AID = 0x%04x\n", rds_info.AID);
319                                         printf("LTN ID = %d (0x%02x)\n", rds_info.LTN, rds_info.LTN);
320                                         printf("Traffic information: ");
321                                         if (rds_info.I)
322                                                 printf("international ");
323                                         if (rds_info.N)
324                                                 printf("national ");
325                                         if (rds_info.R)
326                                                 printf("regional ");
327                                         if (rds_info.U)
328                                                 printf("urban ");
329                                         printf("\n");
330                                 }
331                         }
332                         break;
333                 case GROUP_3B:
334                         if (OutputFlags & RDS_OUTPUT_UNKNGRP)
335                                 printf("group 3B\n");
336                         break;
337                 case GROUP_4A:
338                         //printf("group 4A - time/date\n");
339                         rds_time.tm_sec = 0; // date/time message is sent at 00 secs +/- .2sec
340                         rds_time.tm_min = ((rdsgroup[3] & (0x3f << 6)) >> 6);
341                         rds_time.tm_hour = ((rdsgroup[3] & (0x0f << 12)) >> 12) | ((rdsgroup[2] & 0x01) << 4);
342                         local_time_off = (rdsgroup[3] & 0x0f) * ((rdsgroup[3] & 0x10) ? -1 : 1);
343                         rds_time.tm_hour += local_time_off / 2;
344                         rds_time.tm_min = (rds_time.tm_min + ((local_time_off % 2) * 30)) % 60;
345
346                         day_code = (((rdsgroup[2] & 0xfffe) >> 1) | ((rdsgroup[1] & 0x03) << 15)) /*+ local_time_off*/;
347
348                         year_ = ((float)day_code - 15078.2) / 365.25;
349                         mon_ = ((day_code - 14956.1) - (int)(year_ * 365.25)) / 30.6001;
350                         rds_time.tm_mday = day_code - 14956 - (int)(year_ * 365.25) - (int)(mon_ * 30.6001);
351                         if (mon_ == 14 || mon_ == 15)
352                                 K = 1;
353                         else
354                                 K = 0;
355                         rds_time.tm_year = year_ + K;
356                         rds_time.tm_mon = mon_ - 1 - (K * 12) - 1;
357
358                         rds_time.tm_isdst = -1;
359
360                         if (_rds_private.rds_date_time_cb != NULL)
361                                 _rds_private.rds_date_time_cb(&rds_time, _rds_private.rds_date_time_cb_data);
362
363                         if (OutputFlags & RDS_OUTPUT_DATETIME)
364                                 printf("%s", asctime(&rds_time));
365 #ifdef DEBUG
366                         printf("%d:%02d - local time offset = %d, %s (dcode = %d)\n", rds_time.tm_hour, rds_time.tm_min, local_time_off, asctime(&rds_time), day_code);
367 #endif
368                         //printf("day=%d month=%d year=%d\n", day, mon, year);
369                         break;
370                 case GROUP_4B:
371                         if (OutputFlags & RDS_OUTPUT_UNKNGRP)
372                                 printf("GRP4B\n");
373                         break;
374                 case GROUP_5A:
375                         if (OutputFlags & RDS_OUTPUT_UNKNGRP)
376                                 printf("GRP5A\n");
377                         break;
378                 case GROUP_5B:
379                         if (OutputFlags & RDS_OUTPUT_UNKNGRP)
380                                 printf("GRP5B\n");
381                         break;
382                 case GROUP_6A:
383                         if (OutputFlags & RDS_OUTPUT_UNKNGRP)
384                                 printf("GRP6A\n");
385                         break;
386                 case GROUP_6B:
387                         if (OutputFlags & RDS_OUTPUT_UNKNGRP)
388                                 printf("GRP6B\n");
389                         break;
390                 case GROUP_7A:
391                         if (OutputFlags & RDS_OUTPUT_UNKNGRP)
392                                 printf("GRP7A\n");
393                         break;
394                 case GROUP_7B:
395                         if (OutputFlags & RDS_OUTPUT_UNKNGRP)
396                                 printf("GRP7B\n");
397                         break;
398                 case GROUP_8A:
399                         if (rds_info.LTN != 0) // non TMCpro
400                                 decode_tmc(rdsgroup);
401                         if (rds_info.LTN == 0)
402                                 printf("TMCpro\n");
403                         break;
404                 case GROUP_8B:
405                         if (OutputFlags & RDS_OUTPUT_UNKNGRP)
406                                 printf("GRP8B\n");
407                         break;
408                 case GROUP_9A:
409                         if (OutputFlags & RDS_OUTPUT_UNKNGRP)
410                                 printf("GRP9A\n");
411                         break;
412                 case GROUP_9B:
413                         if (OutputFlags & RDS_OUTPUT_UNKNGRP)
414                                 printf("GRP9B\n");
415                         break;
416                 case GROUP_10A:
417                         if (OutputFlags & RDS_OUTPUT_UNKNGRP)
418                                 printf("GRP10A\n");
419                         break;
420                 case GROUP_10B:
421                         if (OutputFlags & RDS_OUTPUT_UNKNGRP)
422                                 printf("GRP10B\n");
423                         break;
424                 case GROUP_11A:
425                         if (OutputFlags & RDS_OUTPUT_UNKNGRP)
426                                 printf("GRP11A\n");
427                         break;
428                 case GROUP_11B:
429                         if (OutputFlags & RDS_OUTPUT_UNKNGRP)
430                                 printf("GRP11B\n");
431                         break;
432                 case GROUP_12A:
433                         if (OutputFlags & RDS_OUTPUT_UNKNGRP)
434                                 printf("GRP12A\n");
435                         break;
436                 case GROUP_12B:
437                         if (OutputFlags & RDS_OUTPUT_UNKNGRP)
438                                 printf("GRP12B\n");
439                         break;
440                 case GROUP_13A:
441                         if (OutputFlags & RDS_OUTPUT_UNKNGRP)
442                                 printf("GRP13A\n");
443                         break;
444                 case GROUP_13B:
445                         if (OutputFlags & RDS_OUTPUT_UNKNGRP)
446                                 printf("GRP13B\n");
447                         break;
448                 case GROUP_14A: /* EON */ {
449                         unsigned char variant = rdsgroup[1] & 0x0f;
450                         unsigned char AFc1=0, AFc2=0;
451                         float AF1=0, AF2=0;
452                         unsigned short PI;
453
454                         rds_info.PTY = (rdsgroup[1] & 0x3e0) >> 5;
455                         rds_info.TPTN = (rdsgroup[1] & 0x400) >> 10;
456                         rds_info.TPON = (rdsgroup[1] & 0x10) >> 4;
457                         PI = rdsgroup[3];
458
459                         if (variant <= 3) { // PS
460                                 rds_info.PS[variant*2] = ((rdsgroup[2] & 0xff00) >> 8);
461                                 rds_info.PS[(variant*2)+1] = (rdsgroup[2] & 0x00ff);
462                         } else if (variant == 4) {
463                                 printf("EON variant 4\n");
464                         } else  if (variant >= 5 && variant <= 8) { // AF - alternate frequency
465                                 AFc1 = ((rdsgroup[2] & 0xff00) >> 8);
466                                 AFc2 = (rdsgroup[2] & 0x00ff);
467                                 if (AFc1 > 0 && AFc1 < 205)
468                                         AF1 = (float)87.5 + ((float)AFc1 * .1);
469                                 else if (AFc1 >= 225 && AFc1 <= 249)
470                                         AF1 = AFc1 - 224;
471                                 if (AFc2 > 0 && AFc2 < 205)
472                                         AF2 = (float)87.5 + ((float)AFc2 * .1);
473                                 else if (AFc2 >= 225 && AFc2 <= 249)
474                                         AF2 = AFc2 - 224;
475                         }
476                         if (OutputFlags & RDS_OUTPUT_EON) {
477                                 printf("EON var=0x%01x PTY=%d ('%s') PS='%s' AF1=%3.2f (%d) AF2=%3.2f (%d) PI=%d\n", variant, rds_info.PTY, PTY_text[rds_info.PTY], rds_info.PS, AF1, AFc1, AF2, AFc2, PI);
478                         }
479                         } break;
480                 case GROUP_14B: /* Enhanced Other Networks information */
481                         if (OutputFlags & RDS_OUTPUT_EON)
482                                 printf("GRP14B: Enhanced Other Networks information\n");
483                         break;
484                 case GROUP_15A:
485                         if (OutputFlags & RDS_OUTPUT_UNKNGRP)
486                                 printf("GRP15A\n");
487                         break;
488                 case GROUP_15B: /* Fast basic tuning and switching information */
489                         if (OutputFlags & RDS_OUTPUT_UNKNGRP)
490                                 printf("GRP15B: Fast basic tuning and switching information\n");
491                         break;
492                 default:
493                         if (OutputFlags & RDS_OUTPUT_UNKNGRP)
494                                 printf("unkn. RDS group %d\n", grp_type);
495                         break;
496         }
497 }
498