summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/dls.c447
1 files changed, 447 insertions, 0 deletions
diff --git a/src/dls.c b/src/dls.c
new file mode 100644
index 0000000..1a9cc7c
--- /dev/null
+++ b/src/dls.c
@@ -0,0 +1,447 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+
+ dls.c
+ Generete PAD data for DLS
+
+ Authors:
+ Matthias P. Braendli
+ http://opendigitalradio.org
+
+ Sergio Sagliocco <sergio.sagliocco@csp.it>
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <getopt.h>
+
+#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.file\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);
+
+ 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;
+}
+