DAB/DAB+

From Sundtek Wiki
Revision as of 05:34, 8 May 2020 by Sundtek (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

DAB/DAB+

The DAB/DAB+ USB Stick decodes DAB/DAB+ onchip and transfers the decoded audio data fully digital to the host system. Currently Linux and MacOS are supported.

Installation Linux

open a terminal and enter following commands:

sudo -s
wget http://sundtek.de/media/sundtek_netinst.sh
chmod 777 sundtek_netinst.sh
./sundtek_netinst.sh

In case you want to distribute the local stations digitally you can also install our streaming server (again on the commandline)

just run following commands:
sudo -s
/opt/bin/mediaclient --installstreamer

In case you want to use our DAB/DAB+ Player for X86-64 Bit Linux:

http://sundtek.de/media/Sundtek_DAB_FM_Radio-x86_64-2019-07-28_07_05.AppImage

Be sure you set the downloaded binary to executable before trying to run it (right mouse click, permissions, check executable)

Installation MacOSX

MacOSX:

First install the driver, afterwards run the DAB Player. The split driver also allows other application developer to integrate and use those devices.

Installation Raspberry PI

In case you don't want to use our radio application, the installation is the same as on a regular linux system

sudo -s
wget http://sundtek.de/media/sundtek_netinst.sh
chmod 777 sundtek_netinst.sh
./sundtek_netinst.sh

Radio Application:

cd $HOME
sudo apt-get install qt5-default
wget http://sundtek.de/media/dablet_raspbian.tar.gz
tar xf dablet_raspbian.tar.gz
./dablet

DAB/DAB+ commandline interface

scan for transponders:

/opt/bin/mediaclient --scandabfrequencies /dev/dab0 
scanning: 5A / 174928000
scanning: 5B / 176640000
scanning: 5C / 178352000
[LOCKED]
scanning: 5D / 180064000
scanning: 6A / 181936000
scanning: 6B / 183648000
scanning: 6C / 185360000
scanning: 6D / 187072000
scanning: 7A / 188928000
scanning: 7B / 190640000
[LOCKED]
scanning: 7C / 192352000
scanning: 7D / 194064000
[LOCKED]
scanning: 8A / 195936000

tune to a transponder:

/opt/bin/mediaclient -m DAB -f 194064000
Using device: /dev/dab0
Tuning: 194064000
[LOCKED]

turn on direct audio playback in the driver (the driver will try to play back audio via alsa, pulseaudio or oss:

/opt/bin/mediaclient -m DAB -g off

scan a dab transponder:

/opt/bin/mediaclient --scandabservices /dev/dab0 
Service Name, Service ID, Component ID
BR-Klassik         0xd314   0xb
Inforadio          0xd335   0x8
FH Europa          0xd496   0x3
radioeins          0xd332   0x1
radioBERLIN 88 8   0xd321   0x6
kulturradio        0xd323   0x5
Fritz              0xd333   0x7
Antenne            0xd431   0x0
SWR3               0xd3a3   0x4
Bayern 2           0xd412   0xc
WDR2               0xd392   0x2
MDR JUMP           0xd3c2   0xd
rbb TPEG           0xe0d11019   0xc00a
rbb EPG            0xe0d01019   0xc009

Tune to a specific DAB station:

/opt/bin/mediaclient -m DAB -f 194064000 --sid 0xd321
Using device: /dev/dab0
Tuning: 194064000, 0xd321
[LOCKED]


Read signal statistics:

/opt/bin/mediaclient --readsignal=0 -d /dev/dab0
FREQUENCY: 178352000  LOCKED: YES  RSSI: 172  SNR:  10 FIC_QUALITY: 100 CNR: 14
FREQUENCY: 178352000  LOCKED: YES  RSSI: 173  SNR:  10 FIC_QUALITY: 100 CNR: 13
FREQUENCY: 178352000  LOCKED: YES  RSSI: 172  SNR:  10 FIC_QUALITY: 100 CNR: 12
FREQUENCY: 178352000  LOCKED: YES  RSSI: 173  SNR:  10 FIC_QUALITY: 100 CNR: 13
FREQUENCY: 178352000  LOCKED: YES  RSSI: 173  SNR:  10 FIC_QUALITY: 100 CNR: 13
FREQUENCY: 178352000  LOCKED: YES  RSSI: 173  SNR:   9 FIC_QUALITY: 100 CNR: 12
FREQUENCY: 178352000  LOCKED: YES  RSSI: 173  SNR:   9 FIC_QUALITY: 100 CNR: 12

Developer C API

This API works with Linux and MacOS. The Application needs to be linked against libmcsimple.[so/dylib] which connects via unix domain sockets to the driver service which runs as a server. The driver accesses the native USB userspace interface on each system and does not need any kernel driver. It can be found in /opt/lib

The driver package uses LD_PRELOAD, /etc/ld.so.preload, for developers and integrators who directly access the driver this is not needed, be sure you install the driver with the -service flag ./sundtek_netinst.sh -service, full driver releases (no netinst versions) can be downloaded from https://sundtek.de/media The driver package also includes drivers for Sundtek TV devices.

All devices come with a unique serial number, the serial numbers can be used for handling multiple devices and storing unique channel lists.

Basically the API needs only a few commands:

FM Radio:

fd = net_open("/dev/radio0", O_RDWR); // open device node
net_ioctl(fd, command, parameter);
net_close(fd);

For DAB/DAB+

fd = net_open("/dev/dab0", O_RDWR);
net_ioctl(fd, command, parameter);
net_close(fd);

In case you want to access the PCM samples directly you can use net_read(fd, buffer, buflen); on the corresponding device. The driver server has the option to play back the samples directly so no audio handling is needed by the application. It also has the advantage that audio can stay in the background even if the application is closed.

The access to the dab / fm radio node is exclusive, if dab is used fm will be locked and vice versa. Care needs to be taken that some commands might introduce a delay eg. switching the radio mode and changing channels (eg. for DAB/DAB+). Switching from FM<->DAB needs around 4 seconds, changing FM radio station takes less than 100ms, changing DAB Services less than 1 second as long as they're available on the same Ensemble.

Switching the radio mode between DAB and FM will also mute the audio stream, audio has to be unmuted manually by the application.

Additional features are available, eg. detect connect/disconnect devices dynamically. Headers are available in /opt/include, libraries in /opt/lib

Also to note, there might be some other libraries using net_xxx commands, in case you're running into a collision please contact sundtek via email.

Most functions are defined in mediacmds.h and mediaclient.h, some interfaces are derived from the video4linux project videodev2.h (but we have extended them since they aren't completely suitable for our devices).

DAB Scan for Frequencies

example:

int media_scan_dabfrequencies(char *device, int devfd, int console, int running) {
        int fd;
        int rv;
        int nlen;
        char tmp[30];
        if (devfd>=0)
                fd = devfd;
        else
                fd = net_open(device, O_RDWR);

        if (fd>=0) {
                struct dab_frequency dabf;
                struct dab_tuner dabt;
                int i;
                int e;
                int current_scan_index=-1;
                struct dab_scan_setup setup;
                struct dab_scan_parameters parameters;
                memset(&parameters, 0x0, sizeof(struct dab_scan_parameters));
                memset(&setup, 0x0, sizeof(struct dab_scan_setup));
                net_ioctl(fd, DAB_SCAN_SETUP, &setup);

                do {
                        net_ioctl(fd, DAB_SCAN_NEXT_FREQUENCY, &parameters);
                        if (current_scan_index != parameters.scan_index) {
                                if (console>=0) {
                                        sprintf(tmp, "%s %d\n", dab_frequency_list[parameters.scan_index].channel, dab_frequency_list[parameters.scan_index].freq*1000);
                                        rv=write(console, tmp, nlen);
                                } else {
                                        fprintf(stdout, "%s %d\n", dab_frequency_list[parameters.scan_index].channel, dab_frequency_list[parameters.scan_index].freq*1000);
                                        fflush(stdout);
                                }
                        }

                        switch(parameters.status) {
                        case DAB_SCAN_LOCKED:
                        {
                                if (console>=0) {
                                        rv=write(console, "[LOCKED]\n", 9);
                                } else {
                                        fprintf(stdout, "[LOCKED]\n");
                                }
                                break;
                        }
                        case DAB_SCAN_SEARCHING:
                                usleep(10000);
                                break;
                        case DAB_SCAN_COMPLETE:
                        {
                                if (console>=0) {
                                        rv=write(console, "[FINISHED]\n", 11);
                                } else {
                                        fprintf(stdout, "\nScan completed\n");
                                }
                                break;
                        }
                        }
                        current_scan_index = parameters.scan_index;
                        if (console>=0 && running == 0)
                                break;
                } while (parameters.status != DAB_SCAN_COMPLETE);

                if (devfd == -1)
                        net_close(fd);
        }
        return 0;
}

DAB Scan for Services

int media_scan_dabservices(char *device) {
        int fd;
        int rv;
        int i=0;
        fd = net_open(device, O_RDWR);
        if (fd>=0) {
                struct dab_service service;
                printf("Service Name, Service ID, Component ID\n");
                while(1) {
                        service.id=i++;
                        rv = net_ioctl(fd, DAB_GET_SERVICE, &service);
                        if (rv == -1)
                                break;
                        printf("%16s\t0x%x\t0x%x\n", service.service_name, service.sid, service.comp[0]);
                }
                net_close(fd);
        }
        return 0;
}

DAB Get Date

int media_dab_get_date(char *device) {
        int fd;
        struct dab_time t;
        printf("opening device: %s\n", device);
        fd = net_open(device, O_RDWR);
        memset(&t, 0x0, sizeof(struct dab_time));
        if (fd>=0) {
                printf("GET DAB TIME:\n");
                net_ioctl(fd, DAB_GET_TIME, &t);
                printf("%d-%d-%d %d:%d:%d\n", t.year, t.months, t.days, t.hours, t.minutes, t.seconds);
                net_close(fd);
        }
        return 0;
}

DAB Get Ensemble Info

int media_dab_get_ensemble_info(char *device) {
        int fd;
        struct dab_ensemble_info info;
        printf("opening device: %s\n", device);
        fd = net_open(device, O_RDWR);
        if (fd>=0) {
                printf("GET ENSEMBLE INFO:\n");
                net_ioctl(fd, DAB_GET_ENSEMBLE_INFO, &info);
                printf("Ensemble Label: %s\n", info.label);
                net_close(fd);
        }
        return 0;
}

DAB Tune Service

int set_dab_channel(int fd, uint32_t frequency, uint32_t sid, uint8_t sid_set, uint32_t comp, uint8_t comp_set) {
        struct dab_frequency dabf;
        memset(&dabf, 0x0, sizeof(struct dab_frequency));
        if (sid_set && comp_set)
                printf("Tuning: %d, 0x%x, 0x%x\n", frequency, sid, comp);
        else if (sid_set)
                printf("Tuning: %d, 0x%x\n", frequency, sid);
        else
                printf("Tuning: %d\n", frequency);

        dabf.frequency = frequency;
        if (sid_set) {
                dabf.sid_set = 1;
                dabf.sid = sid;
        }
        if (comp_set) {
                dabf.comp = comp;
                dabf.comp_set = 1;
        }
        net_ioctl(fd, DAB_SET_FREQUENCY, &dabf);
        return 0;
}

DAB get Service Data

                struct dab_ensemble_info ens;

                memset(&ens, 0x0, sizeof(struct dab_ensemble_info));
                memset(&data, 0x0, sizeof(struct dab_service_data));
                data.status = 1;

                rv = net_ioctl(radioFD, DAB_GET_DIGITAL_SERVICE_DATA, &data);
                if (rv == 0) {
                    if (data.len > 0) {
                        xdata->status = 0;
                        xdata->type = 0;
                        xdata->len = data.len;

                        rv = net_ioctl(radioFD, DAB_GET_DIGITAL_SERVICE_DATA, xdata);
                        if (rv == 0) {
                            if (xdata->type == MOT) {
                                rv = parse_msc(xdata->data, xdata->len);
                                // please have a look at https://www.etsi.org/deliver/etsi_en/300400_300499/300401/02.01.01_60/en_300401v020101p.pdf how to parse the MSC data.
                                if (rv == 1)
                                    emit finished();

                            } else if (xdata->type == DLS) {
                                if (xdata->len>2) {
                                    if (!(xdata->data[0] & 0x10)) {
                                        QString tmp;
                                        unsigned char *d=reinterpret_cast<unsigned char*>(&xdata->data[1]);
                                        for(int i=0;i<xdata->len-1;i++) {
                                            tmp+=QString::fromUtf16((ushort*)&ebuLatinToUnicode[d[i]], 1);
                                        }
                                        dls = tmp;
                                        emit dlsUpdated();
                                    }
                                }
                            }
                        }
                    }
                }

FM/DAB Mute

int set_mute(int fd, char *arg) {
        int type = 0;
        struct v4l2_control control;
        if (strcmp(arg, "off") == 0) {
                control.id = V4L2_CID_AUDIO_MUTE;
                control.value = 0;
                fprintf(stdout, "Enabling audiostream\n");
                net_ioctl(fd, VIDIOC_S_CTRL, &control);
        } else if (strcmp(arg, "on") == 0) {
                fprintf(stdout, "Disabling audiostream\n");
                control.id = V4L2_CID_AUDIO_MUTE;
                control.value = 1;
                net_ioctl(fd, VIDIOC_S_CTRL, &control);
        } else
                fprintf(stdout, "Wrong argument [%s] choose between on|off\n", arg);

        return 0;
}

FM Scan Frequencies

int media_scan_fmfrequencies(char *device, int devfd, int console, int running) {
        int fd;
        int rv;
        int nlen;
        char tmp[30];
        if (devfd>=0)
                fd = devfd;
        else {
                printf("opening device: %s\n", device);
                fd = net_open(device, O_RDWR);
        }

        if (fd>=0) {
                int i;
                int e;
                int current_scan_index=-1;
                struct fm_scan_setup setup;
                struct fm_scan_parameters parameters;
                memset(&parameters, 0x0, sizeof(struct fm_scan_parameters));
                memset(&setup, 0x0, sizeof(struct fm_scan_setup));
                printf("SCAN SETUP\n");
                net_ioctl(fd, FM_SCAN_SETUP, &setup);

                do {
                        net_ioctl(fd, FM_SCAN_NEXT_FREQUENCY, &parameters);
        //              printf("LOCK STAT: %x - %d - %d\n", parameters.status, parameters.VALID, parameters.READFREQ);
                        switch(parameters.status) {
                        case FM_SCAN_LOCKED:
                        {
                                if (console>=0) {
                                        printf("FREQUENCY: %d ", parameters.READFREQ);
                                        rv=write(console, "[LOCKED]\n", 9);
                                } else {
                                        fprintf(stdout, "%d [LOCKED]\n", parameters.READFREQ);
                                }
                                break;
                        }
                        case FM_SCAN_SEARCHING:
                                usleep(10000);
                                break;
                        case FM_SCAN_COMPLETE:
                        {
                                if (console>=0) {
                                        rv=write(console, "[FINISHED]\n", 11);
                                } else {
                                        fprintf(stdout, "\nScan completed\n");
                                }
                                break;
                        }
                        }
                        if (console>=0 && running == 0)
                                break;
                } while (parameters.status != FM_SCAN_COMPLETE);

                if (devfd == -1)
                        net_close(fd);
        }
        return 0;
}

FM Set Radio Frequency

int set_radio_channel(int fd, int frequency, int tuner) {
        struct v4l2_frequency freq;
        memset(&freq, 0x0, sizeof(struct v4l2_frequency));
        freq.frequency = frequency/1000*16;
        freq.type = V4L2_TUNER_RADIO;
        freq.tuner = tuner;

        net_ioctl(fd, VIDIOC_S_FREQUENCY, &freq);
        return 0;
}

FM Read RDS Data

RDS is read from /dev/radioN, older devices used /dev/rdsN, the RDS format is accordingly to the RDS standard (eg. you might have a look at EN50067_RDS_Standard.pdf)

net_ioctl(rdsfd, FM_RDS_STATUS, &data);
struct fm_rds_data {
        uint8_t rdssync; /* rds synchronized */
        uint8_t pivalid; /* program identification */
        uint8_t tpptyvalid; /* traffic program  / program type*/
        uint16_t PI; /* program identification */
        uint8_t PTY; /* program type */
        uint8_t data[8]; /* representing the RDS blocks */
} __attribute__((packed));
                int i;
                struct rds_data *rdsd;
                struct fm_rds_data data;
                int rdsfd;

                if (strstr(device, "radio")) {
                        rdsfd = net_open(device, O_RDWR);
                        if (rdsfd >= 0) {
                                uint8_t brkstat = 0;
                                uint8_t radiotext[150];
                                uint8_t program[9];
                                uint8_t print_program[9];
                                uint8_t rdsdata[8];
                                memset(radiotext, 0x0, 150);
                                memset(program, 0x0, 9);
                                memset(print_program, 0x0, 9);
                                int x=0;
                                while(1) {
                                        net_ioctl(rdsfd, FM_RDS_STATUS, &data);
                                        if (data.rdssync) {
                                                if (memcmp(rdsdata, data.data, 8)==0)
                                                        continue;
                                                memcpy(rdsdata, data.data, 8);
                                                if ((data.data[2]>>4) == 0) {
                                                        x = (data.data[3]&0x3)*2;
                                                        program[x++] = data.data[6]&0x7f;
                                                        program[x++] = data.data[7]&0x7f;
                                                }
                                                if ((data.data[2]>>4) == 2) {
                                                        x = (data.data[3]&0x0f)*4;
                                                        if ((data.data[3]&0x10) != brkstat) {
                                                                brkstat = data.data[3] & 0x10;
                                                                memset(radiotext, 0x0, 150);
                                                        }
                                                        radiotext[x++]=(data.data[4]&0x7f);
                                                        radiotext[x++]=(data.data[5]&0x7f);
                                                        radiotext[x++]=(data.data[6]&0x7f);
                                                        radiotext[x++]=(data.data[7]&0x7f);
                                                }
                                                usleep(10000);

                                                if (isprint(program[0]) && isprint(program[1])) {
                                                        memcpy(print_program, program, 9);
                                                }

                                                printf("PROGRAM: %s\n", print_program);

                                                printf("RADIOTEXT: ");
                                                for (i=0;i<64;i++) {
                                                        switch(radiotext[i]) {
                                                                case 0x19:
                                                                        printf("ue");
                                                                        break;
                                                                default:
                                                                        printf("%c", radiotext[i]);
                                                        }
                                                }
                                                printf("\n");
                                                fflush(stdout);
                                        }
                                }
                                net_close(rdsfd);

Device detection

Registering a device notification

following command will register a filedescriptor at the driver server to get notified once a device change occurs:

    char ncmd = MEDIA_CMD_NOTIFICATION;
    serverFD = net_connect(0);
    send(serverFD, &ncmd, 1, MSG_NOSIGNAL);
    memset(&pfd, 0x0, sizeof(struct pollfd));
    pfd.fd = serverFD;
    pfd.events = POLLIN|POLLHUP;
Receive Device Attach / Detach Notification
struct media_device_notification {
        uint8_t cmd; // [not used]
        uint8_t status; // [device attach/detach status]
#define DEVICE_ATTACHED 1
#define DEVICE_DETACHED 2
        uint32_t deviceid; // [device id, can also be obtained by /opt/bin/mediaclient -e]
        uint8_t serial[75]; // [Serialnumber]
} __attribute__ ((packed));

        if (serverFD>=0) {
            count = poll(&pfd, 1, 0);
            switch(count) {
            case 0:
                break;
            case -1:
                net_close(serverFD);
                serverFD=-1;
                break;
            default:
                nread = recv(serverFD, buffer, 1024, MSG_DONTWAIT);
                if (nread == sizeof(struct media_device_notification)) {
                    mdn=(struct media_device_notification*)buffer;
                    if (mdn->status == DEVICE_ATTACHED) {
                        qDebug() << "device attached!";
                    }
                    if (mdn->status == DEVICE_DETACHED) {
                        qDebug() << "device detached";
                        removeDevice(reinterpret_cast<char*>(mdn->serial));
                    }
                    checkDevices();
                }
            }
        } else {
            serverFD=net_connect(0);
        }
Check for connected devices
int checkDevices() {
    int i=0;
    int d=0;
    int fd;
    bool found;
    struct media_device_enum *device;
    fd = net_connect(0);
    if (fd<0)
            return fd;
    deviceManager *dm=nullptr;
    while((device=net_device_enum(fd, &i, d))!=0) {
            do {
                    found = false;
                    for(auto iter=m_devices.begin();iter!=m_devices.end();++iter) {
                        dm=(*iter);
                        if (dm->getSerial() == reinterpret_cast<char*>(device->serial)) {
                            found=true;
                            break;
                        }
                    }

                    if (found)
                        continue;

                    if ((device->capabilities & MEDIA_RADIO) ||
                            (device->capabilities & MEDIA_DAB)) {
                        dm = new deviceManager();
                    } else {
                        dm = nullptr;
                    }

                    if (device->capabilities & MEDIA_RADIO) {
                        dm->setFmNode(reinterpret_cast<char*>(device->radio_node));
                            if (device->audio_playback_node[0]) {
                            }
                            if (device->audio_capture_node[0]) {
                            }
                            if (device->capabilities & MEDIA_RDS) {
                            }
                    }

                    if (device->capabilities & MEDIA_DAB) {
                        dm->setDabNode(reinterpret_cast<char*>(device->dab_node));
                    }

                    if (dm) {
                        qDebug() << "added new device " << reinterpret_cast<char*>(device->devicename);
                        dm->setDeviceName(reinterpret_cast<char*>(device->devicename));
                        dm->setSerial(reinterpret_cast<char*>(device->serial));
                        dm->setDeviceId(device->id);
                        m_devices.push_back(dm);
                    }
                    free(device);
            } while((device=net_device_enum(fd, &i, ++d))!=0);
            d=0;
            i++;
    }
    net_close(fd);
   // fprintf(stdout, "\n");

    return 0;
}


etc.

There are a few more features, in case you need more details just contact us.