/* * Copyright (C) 2009, 2010 by Nicole Faerber * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * */ #include #include #include #include #include #include #include #include #include #include "rds.h" #include "bitstream.h" #include "tmc.h" #include "tmc_consts.h" // #define DEBUG 1 static struct TMC_info tmc_info; static struct TMC_msg cur_tmc_msg; static char tmc_msg_str[4096]; static char *tmc_msg_ptr = tmc_msg_str; static struct _TMC_Private { void (*tmc_sid_cb)(char *sid_text, void *user_data); void *tmc_sid_cb_data; void (*tmc_msg_cb)(char *msg, void *user_data); void *tmc_msg_cb_data; } _tmc_private; void tmc_init(void) { memset(&_tmc_private, 0, sizeof(struct _TMC_Private)); } void tmc_set_sid_cb(void (*tmc_sid_cb)(char *sid_text, void *user_data), void *user_data) { _tmc_private.tmc_sid_cb = tmc_sid_cb; _tmc_private.tmc_sid_cb_data = user_data; } static void reset_msg_buf(void) { tmc_msg_ptr = tmc_msg_str; memset(tmc_msg_str, 0, 4096); } void tmc_set_msg_cb(void (*tmc_msg_cb)(char *msg, void *user_data), void *user_data) { reset_msg_buf(); _tmc_private.tmc_msg_cb = tmc_msg_cb; _tmc_private.tmc_msg_cb_data = user_data; } void interpret_tmc(int event, int location, int extent, int CI, int direction) { char sql[256]=""; sqlite3_stmt *ppstmt; int neg_off=0, pos_off=0, lin_ref=0; char road_nr[256]="", fst_name[256]=""; char rdir1[256]="", rdir2[256]=""; char last_name[256]=""; char evt_str[256]=""; char type_str1[128]=""; char type_str2[128]=""; char typechr; int typenr, subtype; //fprintf(stderr, "mCI=%d CI=%d\n", cur_tmc_msg.CI, CI); if (CI != cur_tmc_msg.CI || CI == -1) { #ifdef DEBUG printf("GF evt=%d loc=%d ext=%d CI=%d dir=%d", event, location, extent, CI, direction); printf("ev=%d ", event); #endif snprintf(sql, 256, "select ROADNUMBER,FIRST_NAME,NEGATIVE_OFFSET,POSITIVE_OFFSET,LINEAR_REFERENCE,TYPE,SUBTYPE from lcl where LOCATIONCODE=%d", location); sqlite3_prepare(lcl_db, sql, 256, &ppstmt, NULL); if (sqlite3_step(ppstmt) != SQLITE_ROW) { #ifdef DEBUG printf("lcl.db failed to get location %d event %d\n", location, event); #endif // return; // we cannot do much without location } else { neg_off = sqlite3_column_int(ppstmt, 2); pos_off = sqlite3_column_int(ppstmt, 3); lin_ref = sqlite3_column_int(ppstmt, 4); strncpy(road_nr, (char *)sqlite3_column_text(ppstmt, 0), 255); strncpy(fst_name, (char *)sqlite3_column_text(ppstmt, 1), 255); strncpy(type_str1, (char *)sqlite3_column_text(ppstmt, 5), 128); subtype = sqlite3_column_int(ppstmt, 6); typechr = type_str1[0]; typenr = atoi(&type_str1[1]); #ifdef DEBUG printf(" type=%c %d stype=%d\n", typechr, typenr, subtype); #endif snprintf(sql, 256, "select SNATDESC from lcl_supc where CLASS='%c' AND TCD=%d AND STCD=%d", typechr, typenr, subtype); sqlite3_prepare(lcl_db, sql, 256, &ppstmt, NULL); if (sqlite3_step(ppstmt) != SQLITE_ROW) { #ifdef DEBUG printf("lcl.db failed to get suptype %c %d stype=%d\n", typechr, typenr, subtype); #endif } else { strncpy(type_str1, (char *)sqlite3_column_text(ppstmt, 0), 128); } if (lin_ref != 0) { snprintf(sql, 256, "select FIRST_NAME,SECOND_NAME from lcl where LOCATIONCODE=%d", lin_ref); sqlite3_prepare(lcl_db, sql, 256, &ppstmt, NULL); if (sqlite3_step(ppstmt) != SQLITE_ROW) { #ifdef DEBUG printf("lcl.db failed to get linear ref %d\n", lin_ref); #endif } else { if (!direction) { strncpy(rdir1, (char *)sqlite3_column_text(ppstmt, 1), 255); strncpy(rdir2, (char *)sqlite3_column_text(ppstmt, 0), 255); } else { strncpy(rdir1, (char *)sqlite3_column_text(ppstmt, 0), 255); strncpy(rdir2, (char *)sqlite3_column_text(ppstmt, 1), 255); } } } if (extent > 0) { // printf("%s ab %s ", road_nr, fst_name); if (direction) { /* neg */ while (extent--) { snprintf(sql, 256, "select ROADNUMBER,FIRST_NAME,NEGATIVE_OFFSET,POSITIVE_OFFSET,TYPE,SUBTYPE from lcl where LOCATIONCODE=%d", neg_off); sqlite3_prepare(lcl_db, sql, 256, &ppstmt, NULL); if (sqlite3_step(ppstmt) != SQLITE_ROW) { #ifdef DEBUG printf("lcl.db failed to get location %d\n", location); #endif break; } else neg_off = sqlite3_column_int(ppstmt, 2); } //printf("bis %s\n", (char *)sqlite3_column_text(ppstmt, 1)); } else { /* pos */ while (extent--) { snprintf(sql, 256, "select ROADNUMBER,FIRST_NAME,NEGATIVE_OFFSET,POSITIVE_OFFSET,TYPE,SUBTYPE from lcl where LOCATIONCODE=%d", pos_off); sqlite3_prepare(lcl_db, sql, 256, &ppstmt, NULL); if (sqlite3_step(ppstmt) != SQLITE_ROW) { #ifdef DEBUG printf("lcl.db failed to get location %d\n", location); #endif break; } else pos_off = sqlite3_column_int(ppstmt, 3); } //printf("bis %s\n", (char *)sqlite3_column_text(ppstmt, 1)); } strncpy(last_name, (char *)sqlite3_column_text(ppstmt, 1), 255); extent=1; strncpy(type_str2, (char *)sqlite3_column_text(ppstmt, 4), 128); subtype = sqlite3_column_int(ppstmt, 5); typechr = type_str2[0]; typenr = atoi(&type_str2[1]); // printf(" type=%c %d stype=%d\n", typechr, typenr, subtype); snprintf(sql, 256, "select SNATDESC from lcl_supc where CLASS='%c' AND TCD=%d AND STCD=%d", typechr, typenr, subtype); sqlite3_prepare(lcl_db, sql, 256, &ppstmt, NULL); if (sqlite3_step(ppstmt) != SQLITE_ROW) { #ifdef DEBUG printf("lcl.db failed to get suptype %c %d stype=%d\n", typechr, typenr, subtype); #endif } else { strncpy(type_str2, (char *)sqlite3_column_text(ppstmt, 0), 128); } } } snprintf(sql, 256, "select TEXT_DE_NOQUANT from evl where CODE=%d", event); sqlite3_prepare(lcl_db, sql, 256, &ppstmt, NULL); if (sqlite3_step(ppstmt) != SQLITE_ROW) { #ifdef DEBUG printf("lcl.db failed to get event %d\n", event); #endif } else { strncpy(evt_str, (char *)sqlite3_column_text(ppstmt, 0), 255); } if (CI != -1) cur_tmc_msg.CI = CI; // here goes the message ;) if (lin_ref != 0) tmc_msg_ptr += sprintf(tmc_msg_ptr, "%s %s - %s,\n", road_nr, rdir1, rdir2); if (location == 64777) { tmc_msg_ptr += sprintf(tmc_msg_ptr, "%s\n", evt_str); } else { if (extent) { #ifdef DEBUG printf("dir=%d\n", direction); #endif if (direction) tmc_msg_ptr += sprintf(tmc_msg_ptr, "zwischen %s %s und %s %s\n%s\n", type_str1, fst_name, type_str2, last_name, evt_str); else tmc_msg_ptr += sprintf(tmc_msg_ptr, "zwischen %s %s und %s %s\n%s\n", type_str2, last_name, type_str1, fst_name, evt_str); } else tmc_msg_ptr += sprintf(tmc_msg_ptr, "in Höhe %s %s\n%s\n", type_str1, fst_name, evt_str); } } } void parse_tmc_single(unsigned short *rdsgroup) { unsigned short event = cur_tmc_msg.msgs[0][1] & 0x7FF; unsigned short location = cur_tmc_msg.msgs[0][2]; unsigned char extent = (cur_tmc_msg.msgs[0][1] & ((0x07) << 11)) >> 11; unsigned char dir = (cur_tmc_msg.msgs[0][1] & ((0x01) << 14)) >> 14; static unsigned short oevent=0; static unsigned short olocation=0; #ifdef DEBUG printf(" single group\n"); #endif if (olocation != location && oevent != event) { interpret_tmc(event, location, extent, -1, dir); olocation = location; oevent = event; if (_tmc_private.tmc_msg_cb != NULL) { _tmc_private.tmc_msg_cb(tmc_msg_str, _tmc_private.tmc_msg_cb_data); } reset_msg_buf(); } } char *replace_str(char *str, char *orig, char *rep) { static char buffer[4096]; char *p; if(!(p = strstr(str, orig))) // Is 'orig' even in 'str'? return str; strncpy(buffer, str, p-str); // Copy characters from 'str' start to 'orig' st$ buffer[p-str] = '\0'; sprintf(buffer+(p-str), "%s%s", rep, p+strlen(orig)); return buffer; } void interpret_tmc_multi(void) { int i; unsigned short event = cur_tmc_msg.msgs[0][1] & 0x7FF; unsigned short location = cur_tmc_msg.msgs[0][2]; unsigned char extent = (cur_tmc_msg.msgs[0][1] & (0x07 << 11)) >> 11; unsigned char dir = (cur_tmc_msg.msgs[0][1] & (0x01 << 14)) >> 14; unsigned char label; unsigned short aevent; char sql[256]=""; sqlite3_stmt *ppstmt; char evt_str[256]=""; char lofrstr[16]=""; unsigned char ext_buf[256]; #ifdef DEBUG printf("interpret_tmc_multi: sz=%d\n", cur_tmc_msg.msgsz); for (i=0; i> 8; ext_buf[(i*4)+1] = cur_tmc_msg.msgs[i+1][1] & 0xff; ext_buf[(i*4)+2] = (cur_tmc_msg.msgs[i+1][2] & 0xff00) >> 8; ext_buf[(i*4)+3] = cur_tmc_msg.msgs[i+1][2] & 0xff; }; bitstream_start(ext_buf, cur_tmc_msg.msgsz*4); bitstream_get_next_bits(4); while (bitstream_bits_remaining() > 0) { label = bitstream_get_next_bits(4); #ifdef DEBUG if (OutputFlags & RDS_OUTPUT_TMC) { printf("o '%s' %x\n", EVNT_LABEL[label], label); } #endif if (label == 0) { unsigned char dur=0; dur = bitstream_get_next_bits(3); tmc_msg_ptr += sprintf(tmc_msg_ptr, " dur=%d\n", dur); } if (label == 1) { unsigned char ctrlcd=0; ctrlcd = bitstream_get_next_bits(3); if (ctrlcd == 2) tmc_msg_ptr += sprintf(tmc_msg_ptr, "in beiden Richtungen\n"); else tmc_msg_ptr += sprintf(tmc_msg_ptr, " ctrlcd=%d\n", ctrlcd); } if (label == 2) { unsigned int lofr=0; lofr = bitstream_get_next_bits(5); if (lofr == 0) { printf("shit - more than 100km\n"); strcpy(lofrstr, ">100 km"); } if (lofr>0 && lofr<11) { printf("lofr = %dkm\n", lofr); sprintf(lofrstr, "%d km", lofr); } if (lofr>10 && lofr<16) { printf("lofr = %dkm\n", 12 + ((lofr-11)*2)); sprintf(lofrstr, "%d km", 12 + ((lofr-11)*2)); } if (lofr>15 && lofr<32) printf("lofr = %dkm\n", 25 + ((lofr-16)*5)); sprintf(lofrstr, "%d km", 25 + ((lofr-16)*5)); } if (label == 3) { unsigned int slim=0; slim = bitstream_get_next_bits(5); if (slim > 0) tmc_msg_ptr += sprintf(tmc_msg_ptr, " slim = %d km/h\n", slim*5); } if (label == 4) { unsigned int quant5=0; quant5 = bitstream_get_next_bits(5); tmc_msg_ptr += sprintf(tmc_msg_ptr, " quant5 = %d km\n", quant5); } if (label == 5) { unsigned int quant8=0; quant8 = bitstream_get_next_bits(8); tmc_msg_ptr += sprintf(tmc_msg_ptr, " quant8 = %d km\n", quant8); } if (label == 6) { unsigned char supinfo=0; supinfo = bitstream_get_next_bits(8); #ifdef DEBUG printf(" supinfo = %d\n", supinfo); #endif snprintf(sql, 256, "select TEXT_DE from ev_sup where CODE=%d", supinfo); sqlite3_prepare(lcl_db, sql, 256, &ppstmt, NULL); if (sqlite3_step(ppstmt) != SQLITE_ROW) { #ifdef DEBUG printf("lcl.db failed to get ev_sup %d\n", supinfo); #endif } else { strncpy(evt_str, (char *)sqlite3_column_text(ppstmt, 0), 255); tmc_msg_ptr += sprintf(tmc_msg_ptr, "%s\n", evt_str); } } if (label == 7) { unsigned int stime=0; stime = bitstream_get_next_bits(8); // printf(" stime = %d\n", stime); if (stime < 96) { stime *= 15; // 15 min internval tmc_msg_ptr += sprintf(tmc_msg_ptr, " ab %02d:%02d\n", (stime / 60), (stime % 60)); } else if (stime < 201) { stime -= 96; tmc_msg_ptr += sprintf(tmc_msg_ptr, " ab %d Tage, %d:00 Uhr\n", (stime / 24), (stime % 24)); } else if (stime < 232) { stime -= 201; tmc_msg_ptr += sprintf(tmc_msg_ptr, " ab dem %d.\n", stime+1); } else { stime -= 232; tmc_msg_ptr += sprintf(tmc_msg_ptr, " ab %s. %s\n", (stime % 2) ? "31":"15", monthname[stime / 2]); } } if (label == 8) { unsigned int etime=0; etime = bitstream_get_next_bits(8); // printf(" etime = %d\n", etime); if (etime < 96) { etime *= 15; // 15 min internval tmc_msg_ptr += sprintf(tmc_msg_ptr, " bis vor. %02d:%02d\n", (etime / 60), (etime % 60)); } else if (etime < 201) { etime -= 96; tmc_msg_ptr += sprintf(tmc_msg_ptr, " bis %d Tage, %d:00 Uhr\n", (etime / 24), (etime % 24)); } else if (etime < 232) { etime -= 201; tmc_msg_ptr += sprintf(tmc_msg_ptr, " bis zum %d.\n", etime+1); } else { etime -= 232; tmc_msg_ptr += sprintf(tmc_msg_ptr, " bis etwa %s. %s\n", (etime % 2) ? "31":"15", monthname[etime / 2]); } } if (label == 9) { aevent = bitstream_get_next_bits(11); #ifdef DEBUG printf(" aevent = %d ", aevent); #endif snprintf(sql, 256, "select TEXT_DE_NOQUANT from evl where CODE=%d", aevent); sqlite3_prepare(lcl_db, sql, 256, &ppstmt, NULL); if (sqlite3_step(ppstmt) != SQLITE_ROW) { #ifdef DEBUG printf("-lcl.db failed to get event %d\n", aevent); #endif } else { strncpy(evt_str, (char *)sqlite3_column_text(ppstmt, 0), 255); // printf("(L)='%s'\n", evt_str); if (strlen(lofrstr) != 0 && strstr(evt_str,"(L)")!=NULL) { replace_str(evt_str, "(L)", lofrstr); } tmc_msg_ptr += sprintf(tmc_msg_ptr, "%s\n", evt_str); } } if (label == 10) { unsigned char div; div = bitstream_get_next_bits(16); tmc_msg_ptr += sprintf(tmc_msg_ptr, " div = %d\n", div); } if (label == 11) { unsigned char dest; dest = bitstream_get_next_bits(16); tmc_msg_ptr += sprintf(tmc_msg_ptr, " dest = %d\n", dest); } if (label == 12) { unsigned char res; res = bitstream_get_next_bits(16); tmc_msg_ptr += sprintf(tmc_msg_ptr, " res = %d\n", res); } if (label == 13) { unsigned char clink; clink = bitstream_get_next_bits(16); tmc_msg_ptr += sprintf(tmc_msg_ptr, " clink = %d\n", clink); } if (label == 14) { // tmc_msg_ptr += sprintf(tmc_msg_ptr, " seperator\n"); } if (label == 15) { #ifdef DEBUG printf(" res\n"); #endif } } #ifdef DEBUGx if (cur_tmc_msg.msgsz > 2) { for (i=2; i> 12; static unsigned char oGSI = 0; #ifdef DEBUG unsigned char CI = rdsgroup[1] & 0x07; #endif #ifdef DEBUG printf(" multi CI=%d ", CI); #endif if (rdsgroup[2] & 0x8000) { if (cur_tmc_msg.msgsz > 0) { interpret_tmc_multi(); cur_tmc_msg.msgsz = 0; } #ifdef DEBUG printf(" #1 GSI=%d\n", GSI); #endif oGSI = 0; memset(&cur_tmc_msg.msgs, 0, 6*3*sizeof(short)); cur_tmc_msg.msgs[0][0] = rdsgroup[1]; cur_tmc_msg.msgs[0][1] = rdsgroup[2]; cur_tmc_msg.msgs[0][2] = rdsgroup[3]; //msgsz = 1; } else { if (rdsgroup[2] & 0x4000) { // printf(" #2 GSI=%d\n", GSI); cur_tmc_msg.msgs[1][0] = rdsgroup[1]; cur_tmc_msg.msgs[1][1] = rdsgroup[2]; cur_tmc_msg.msgs[1][2] = rdsgroup[3]; cur_tmc_msg.msgsz = 2; } else { if (oGSI == 0) { oGSI = GSI; } // printf(" #%d GSI=%d (oGSI=%d)\n", 2+(oGSI-GSI), GSI, oGSI); cur_tmc_msg.msgs[2+(oGSI-GSI)][0] = rdsgroup[1]; cur_tmc_msg.msgs[2+(oGSI-GSI)][1] = rdsgroup[2]; cur_tmc_msg.msgs[2+(oGSI-GSI)][2] = rdsgroup[3]; cur_tmc_msg.msgsz = 2+(oGSI-GSI); } } // printf(" 0x%04x 0x%04x\n", rdsgroup[2], rdsgroup[3]); } // enum TMCtype { TMC_GROUP=0, TMC_SINGLE, TMC_SYSTEM, TMC_TUNING }; void decode_tmc(unsigned short *rdsgroup) { unsigned char X4 = (rdsgroup[1] & 0x10) >> 4; unsigned char X3 = (rdsgroup[1] & 0x08) >> 3; unsigned char X0X2 = rdsgroup[1] & 0x07; //static unsigned char tmc_provider[9] = ""; if (X4 == 0) { // User message if (X3 == 1) { // single group msg parse_tmc_single(rdsgroup); } else { // multi group msg if (X0X2 > 0) { parse_tmc_multi(rdsgroup); } #ifdef DEBUG else printf("err... X0X2(CI) = %d\n", X0X2); #endif } } else { if (X3 == 1) { //printf(" tuning?\n"); } else { // printf(" system "); if (X0X2 == 4) { tmc_info.provider_str[0] = (rdsgroup[2] & 0xff00) >> 8; tmc_info.provider_str[1] = rdsgroup[2] & 0xff; tmc_info.provider_str[2] = (rdsgroup[3] & 0xff00) >> 8; tmc_info.provider_str[3] = rdsgroup[3] & 0xff; if (_tmc_private.tmc_sid_cb != NULL) _tmc_private.tmc_sid_cb(tmc_info.provider_str, _tmc_private.tmc_sid_cb_data); if (OutputFlags & RDS_OUTPUT_TMC) printf("provider = '%s'\n", tmc_info.provider_str); } else if (X0X2 == 5) { tmc_info.provider_str[4] = (rdsgroup[2] & 0xff00) >> 8; tmc_info.provider_str[5] = rdsgroup[2] & 0xff; tmc_info.provider_str[6] = (rdsgroup[3] & 0xff00) >> 8; tmc_info.provider_str[7] = rdsgroup[3] & 0xff; if (_tmc_private.tmc_sid_cb != NULL) _tmc_private.tmc_sid_cb(tmc_info.provider_str, _tmc_private.tmc_sid_cb_data); if (OutputFlags & RDS_OUTPUT_TMC) printf("provider = '%s'\n", tmc_info.provider_str); } #ifdef DEBUG else printf("X0X2(CI)=%d \n", X0X2); #endif } } }