/* Copyright (C) 2014 CSP Innovazione nelle ICT s.c.a r.l. (http://rd.csp.it/) 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 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . dls.c Generete PAD data for DLS Authors: Matthias P. Braendli http://opendigitalradio.org Sergio Sagliocco */ #include #include #include #include #include #include #include #include #include "lib_crc.h" #define MIN(a,b) (((a)<(b))?(a):(b)) #define MAX(a,b) (((a)>(b))?(a):(b)) #define MAXSEGLEN 8179 #define MAXDLS 129 typedef unsigned char UCHAR; typedef unsigned short int USHORT; void create_dls_datagroup (char* text, int padlen, unsigned char*** p_dlsdg, int* p_numdg); void writeDLS(int output_fd, const char* dls_file, int padlen); int get_xpadlengthmask(int padlen); #define ALLOWED_PADLEN "23, 26, 34, 42, 58" void usage(char* name) { fprintf(stderr, "DAB DLS encoder %s\n\n" "By CSP Innovazione nelle ICT s.c.a r.l. (http://rd.csp.it/)\n" "and Opendigitalradio.org\n\n" "Reads DLS from /tmp/dls.txt\n\n" "WARNING: This program has memory leaks! Do not attempt\n" "to leave it running for long periods of time!\n\n" " http://opendigitalradio.org\n\n", #if defined(GITVERSION) GITVERSION #else PACKAGE_VERSION #endif ); fprintf(stderr, "Usage: %s [OPTIONS...]\n", name); fprintf(stderr, " -o, --output=FILENAME Fifo to write PAD data into.\n" " Default: /tmp/pad.fifo\n" " -t, --dls=FILENAME Fifo or file to read DLS text from.\n" " Default: /tmp/dls.txt\n" " -p, --pad=LENGTH Set the pad length.\n" " Possible values: " ALLOWED_PADLEN "\n" " Default: 58\n" ); } #define no_argument 0 #define required_argument 1 #define optional_argument 2 int main(int argc, char *argv[]) { int len, ret; int padlen=58; char* output = "/tmp/pad.fifo"; char* dls_file = "/tmp/dls.txt"; const struct option longopts[] = { {"output", required_argument, 0, 'o'}, {"dls", required_argument, 0, 't'}, {"pad", required_argument, 0, 'p'}, {"help", no_argument, 0, 'h'}, {0,0,0,0}, }; if (argc < 2) { fprintf(stderr, "Error: too few arguments!\n"); usage(argv[0]); return 2; } int ch=0; int index; while(ch != -1) { ch = getopt_long(argc, argv, "ho:t:p:", longopts, &index); switch (ch) { case 'o': output = optarg; break; case 't': dls_file = optarg; break; case 'p': padlen = atoi(optarg); break; case '?': case 'h': usage(argv[0]); return 0; } } if (get_xpadlengthmask(padlen) == -1) { fprintf(stderr, "Error: pad length %d invalid: Possible values: " ALLOWED_PADLEN "\n", padlen); return 2; } int output_fd = open(output, O_WRONLY); if (output_fd == -1) { perror("Failed to open output"); return 3; } writeDLS(output_fd, dls_file, padlen); /* Do it twice, so that there is for sure one toggle done * by create_dls_datagroup * * It's ugly, I know. */ writeDLS(output_fd, dls_file, padlen); return 0; } void writeDLS(int output_fd, const char* dls_file, int padlen) { char dlstext[MAXDLS]; static char dlstextprev[MAXDLS]; int dlslen; int i; static unsigned char** dlsdg; static int numdg = 0; static int dlsfd = 0; if (dlsfd!=0) { close(dlsfd); } dlsfd = open(dls_file, O_RDONLY); if (dlsfd == -1) { fprintf(stderr,"Error - Cannot open dls file\n"); return; } dlslen = read(dlsfd, dlstext, MAXDLS); dlstext[dlslen] = 0x00; //if (strcmp(dlstext,dlstextprev)!=0) { create_dls_datagroup(dlstext, padlen, &dlsdg, &numdg); strcpy(dlstextprev, dlstext); //} for (i = 0; i < numdg; i++) { write(output_fd, dlsdg[i], padlen); } } void create_dls_datagroup (char* text, int padlen, UCHAR*** p_dlsdg, int* p_numdg) { UCHAR dlsseg[8][16]; // max 8 segments, each max 16 chars UCHAR** dlsdg; // Array of datagroups composing dls text; int numseg; // Number of DSL segments int lastseglen; // Length of the last segment int numdg; // Number of data group int xpadlengthmask; int i, j, k, z, idx_start_crc, idx_stop_crc; USHORT dlscrc; static UCHAR toggle = 0; if (toggle == 0) toggle = 1; else toggle = 0; numseg = strlen(text) / 16; lastseglen = strlen(text) % 16; if (padlen-9 >= 16) { if (lastseglen > 0) { numseg++; // The last incomplete segment } // The PAD can contain the full segmnet and overhead (9 bytes) numdg = numseg; } else { // Each 16 char segment span over 2 dg numdg = numseg * 2; if (lastseglen > 0) { numseg++; // The last incomplete segment if (lastseglen <= padlen-9) { numdg += 1; } else { numdg += 2; } } } *p_numdg = numdg; fprintf(stderr, "PAD Length: %d\n", padlen); fprintf(stderr, "DLS text: %s\n", text); fprintf(stderr, "Number of DLS segments: %d\n", numseg); fprintf(stderr, "Number of DLS data groups: %d\n", numdg); xpadlengthmask = get_xpadlengthmask(padlen); *p_dlsdg = (UCHAR**) malloc(numdg * sizeof(UCHAR*)); dlsdg = *p_dlsdg; i = 0; for (z=0; z < numseg; z++) { char* curseg; int curseglen; UCHAR firstseg, lastseg; curseg = &text[z * 16]; fprintf(stderr, "Segment number %d\n", z+1); if (z == 0) { // First segment firstseg = 1; } else { firstseg = 0; } if (z == numseg-1) { //Last segment if (lastseglen != 0) { curseglen = lastseglen; } else { curseglen = 16; } lastseg = 1; } else { curseglen = 16; lastseg = 0; } if (curseglen <= padlen-9) { // Segment is composed of 1 data group dlsdg[i] = (UCHAR*) malloc(padlen * sizeof(UCHAR)); // FF-PAD Byte L (CI=1) dlsdg[i][padlen-1]=0x02; // FF-PAD Byte L-1 (Variable size X_PAD) dlsdg[i][padlen-2]=0x20; // CI => data length = 12 (011) - Application Type=2 // (DLS - start of X-PAD data group) dlsdg[i][padlen-3]=(xpadlengthmask<<5) | 0x02; // End of CI list dlsdg[i][padlen-4]=0x00; // DLS Prefix (T=1,Only one segment,segment length-1) dlsdg[i][padlen-5]=((toggle*8+firstseg*4+lastseg*2+0)<<4) | (curseglen-1); if (firstseg==1) { // DLS Prefix (Charset standard) dlsdg[i][padlen-6]=0x00; } else { // DLS SegNum dlsdg[i][padlen-6]=z<<4; } // CRC start from prefix idx_start_crc = padlen-5; // DLS text for (j = 0; j < curseglen; j++) { dlsdg[i][padlen-7-j] = curseg[j]; } idx_stop_crc = padlen - 7 - curseglen+1; dlscrc = 0xffff; for (j = idx_start_crc; j >= idx_stop_crc; j--) { dlscrc = update_crc_ccitt(dlscrc, dlsdg[i][j]); } dlscrc = ~dlscrc; fprintf(stderr, "crc=%x ~crc=%x\n", ~dlscrc, dlscrc); dlsdg[i][padlen-7-curseglen] = (dlscrc & 0xFF00) >> 8; // HI CRC dlsdg[i][padlen-7-curseglen-1] = dlscrc & 0x00FF; // LO CRC // NULL PADDING for (j = padlen-7-curseglen-2; j >= 0; j--) { dlsdg[i][j]=0x00; } fprintf(stderr, "Data group: "); for (j = 0; j < padlen; j++) fprintf(stderr, "%x ", dlsdg[i][j]); fprintf(stderr, "\n"); i++; } else { // Segment is composed of 2 data groups // FIRST DG (NO CRC) dlscrc = 0xffff; dlsdg[i] = (UCHAR*) malloc(padlen * sizeof(UCHAR)); // FF-PAD Byte L (CI=1) dlsdg[i][padlen-1]=0x02; // FF-PAD Byte L-1 (Variable size X_PAD) dlsdg[i][padlen-2]=0x20; // CI => data length = 12 (011) - Application Type=2 // (DLS - start of X-PAD data group) dlsdg[i][padlen-3]=(xpadlengthmask<<5) | 0x02; // End of CI list dlsdg[i][padlen-4]=0x00; // DLS Prefix (T=1,Only one segment,segment length-1) dlsdg[i][padlen-5]=((toggle*8+firstseg*4+lastseg*2+0)<<4) | (curseglen-1); if (firstseg == 1) { // DLS Prefix (Charset standard) dlsdg[i][padlen-6] = 0x00; } else { // DLS SegNum dlsdg[i][padlen-6]=(i-1)<<4; } dlscrc = update_crc_ccitt(dlscrc, dlsdg[i][padlen-5]); dlscrc = update_crc_ccitt(dlscrc, dlsdg[i][padlen-6]); // DLS text for (j=0; j < MIN(curseglen, padlen-7); j++) { dlsdg[i][padlen-7-j] = curseg[j]; dlscrc = update_crc_ccitt(dlscrc, dlsdg[i][padlen-7-j]); } k = j; // end of segment if (curseglen == padlen-8) { dlscrc = ~dlscrc; dlsdg[i][1] = (dlscrc & 0xFF00) >> 8; // HI CRC fprintf(stderr, "crc=%x ~crc=%x\n", ~dlscrc, dlscrc); } else if (curseglen == padlen-7) { dlscrc = ~dlscrc; fprintf(stderr, "crc=%x ~crc=%x\n", ~dlscrc, dlscrc); } dlsdg[i][0]=0x00; fprintf(stderr, "First Data group: "); for (j = 0; j < padlen; j++) fprintf(stderr, "%x ", dlsdg[i][j]); fprintf(stderr,"\n"); // SECOND DG (NO CI, NO PREFIX) i++; dlsdg[i] = (UCHAR*) malloc(padlen*sizeof(UCHAR)); // FF-PAD Byte L (CI=0) dlsdg[i][padlen-1] = 0x00; // FF-PAD Byte L-1 (Variable size X_PAD) dlsdg[i][padlen-2] = 0x20; if (curseglen == padlen-8) { dlsdg[i][padlen-3] = dlscrc & 0x00FF; // LO CRC } else if (curseglen==padlen-7) { dlsdg[i][padlen-3] = (dlscrc & 0xFF00) >> 8; // HI CRC dlsdg[i][padlen-4] = dlscrc & 0x00FF; // LO CRC } else { // DLS text for (j = 0; j < curseglen-k; j++) { dlsdg[i][padlen-3-j] = curseg[k+j]; dlscrc = update_crc_ccitt(dlscrc, dlsdg[i][padlen-3-j]); } dlscrc = ~dlscrc; dlsdg[i][padlen-3-curseglen+k] = (dlscrc & 0xFF00) >> 8; // HI CRC dlsdg[i][padlen-3-curseglen+k-1] = dlscrc & 0x00FF; // LO CRC } fprintf(stderr, "Second Data group: "); for (j = 0; j < padlen; j++) fprintf(stderr, "%x ", dlsdg[i][j]); fprintf(stderr, "\n"); fprintf(stderr, "**** crc=%x ~crc=%x\n", ~dlscrc, dlscrc); i++; } } } int get_xpadlengthmask(int padlen) { int xpadlengthmask; /* Don't forget to change ALLOWED_PADLEN * if you change this check */ if (padlen == 23) xpadlengthmask = 3; else if (padlen == 26) xpadlengthmask = 4; else if (padlen == 34) xpadlengthmask = 5; else if (padlen == 42) xpadlengthmask = 6; else if (padlen == 58) xpadlengthmask = 7; else xpadlengthmask = -1; // Error return xpadlengthmask; }