diff options
| -rw-r--r-- | .gitignore | 18 | ||||
| -rw-r--r-- | b100-2048.ini | 25 | ||||
| -rw-r--r-- | b200-2048-zmq.ini | 20 | ||||
| -rw-r--r-- | b200-2048.ini | 6 | ||||
| -rw-r--r-- | b200-30.72.ini | 25 | ||||
| -rw-r--r-- | b200-resample.ini | 6 | ||||
| -rw-r--r-- | b200-sync-zmq.ini | 75 | ||||
| -rw-r--r-- | b200-sync.ini | 6 | ||||
| -rw-r--r-- | edi-test.mux | 163 | ||||
| -rwxr-xr-x | edi/edidebug.py | 3 | ||||
| -rwxr-xr-x | edi/edisend.py | 193 | ||||
| -rwxr-xr-x | encode-alsa32.sh | 17 | ||||
| -rwxr-xr-x | encode-alsasrc-gst-dabplus.sh | 39 | ||||
| -rwxr-xr-x | encode-alsasrc-sox-mpeg.sh | 22 | ||||
| -rwxr-xr-x | encode-fb.sh | 3 | ||||
| -rwxr-xr-x | encode-fbplus.sh | 36 | ||||
| -rwxr-xr-x | encode-jack.sh | 154 | ||||
| -rwxr-xr-x | encode-stdin.sh | 3 | ||||
| -rwxr-xr-x | encode-url-gst-dabplus.sh | 44 | ||||
| -rwxr-xr-x | histogram.py | 28 | ||||
| -rw-r--r-- | minimal.ini | 11 | ||||
| -rw-r--r-- | modulatoroffset | 2 | ||||
| -rwxr-xr-x | mpeg_analyse.py | 117 | ||||
| -rw-r--r-- | rdsparse/decoder_impl.cc | 21 | ||||
| -rw-r--r-- | test.ini | 31 | ||||
| -rw-r--r-- | zmq-simul.mux | 94 | 
26 files changed, 768 insertions, 394 deletions
| diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fce88b8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,18 @@ +zmqtest +eti +*.sub +*.pub +*.raw +*.wav +*.mp2 +*.dab +*.dabp +*.dabplus +keys +filter +rdsparse/CMakeCache.txt +rdsparse/CMakeFiles/ +rdsparse/Makefile +rdsparse/cmake_install.cmake +rdsparse/cmake_uninstall.cmake +rdsparse/rdsparse diff --git a/b100-2048.ini b/b100-2048.ini index 7d4b94c..f990a2d 100644 --- a/b100-2048.ini +++ b/b100-2048.ini @@ -10,11 +10,12 @@ filelog=1  filename=/dev/stderr  [input] -transport=file -source=/dev/stdin -;transport=zeromq -;source=tcp://mpb.li:9100 -loop=0 +;transport=file +;source=/dev/stdin +;loop=0 +transport=zeromq +source=tcp://core.mpb.li:9100 +max_frames_queued=400  [modulator]  ; Gain mode: 0=FIX, 1=MAX, 2=VAR @@ -33,8 +34,8 @@ digital_gain=1.0  rate=2048000  [firfilter] -enabled=0 -filtertapsfile=filter/simplefiltertaps.txt +enabled=1 +filtertapsfile=filter/filtertaps.txt  [output]  ; choose output: possible values: uhd, file @@ -48,7 +49,7 @@ filename=/dev/null  device=  master_clock_rate=20480000  type=b100 -txgain=4 +txgain=12  ;frequency=234208000  channel=13C @@ -64,10 +65,6 @@ behaviour_refclk_lock_lost=crash  ; Used for SFN with the UHD output  [delaymanagement] -synchronous=0 +synchronous=1 -; choose between fixed and dynamic offset definition -management=dynamic - -fixedoffset=0.002 -dynamicoffsetfile=modulatoroffset +offset=2.0 diff --git a/b200-2048-zmq.ini b/b200-2048-zmq.ini index 905454f..f79f0c3 100644 --- a/b200-2048-zmq.ini +++ b/b200-2048-zmq.ini @@ -1,4 +1,5 @@ -; Sample configuration file for CRC-DABMOD +; Sample configuration file for ODR-DabMod +; using a zeromq input  [remotecontrol]  telnet=1 @@ -6,7 +7,7 @@ telnetport=2121  [log]  syslog=0 -filelog=1 +filelog=0  filename=/dev/stderr  [input] @@ -15,6 +16,7 @@ filename=/dev/stderr  transport=zeromq  source=tcp://localhost:9100  loop=0 +max_frames_queued=400  [modulator]  ; Gain mode: 0=FIX, 1=MAX, 2=VAR @@ -22,12 +24,12 @@ gainmode=2  ; Transmission mode  ; If not defined, take the mode from ETI -;mode=2 +mode=1  ; Set to 0 to disable CicEqualiser  dac_clk_rate=0 -digital_gain=1.0 +digital_gain=0.8  ; Output sample rate  rate=2048000 @@ -49,7 +51,7 @@ filename=/dev/null  device=  master_clock_rate=32768000  type=b200 -txgain=45 +txgain=50  ;frequency=234208000  channel=13C @@ -63,12 +65,10 @@ pps_source=none  ; possible values: ignore, crash  behaviour_refclk_lock_lost=crash +max_gps_holdover_time=300 +  ; Used for SFN with the UHD output  [delaymanagement]  synchronous=0 -; choose between fixed and dynamic offset definition -management=dynamic - -fixedoffset=0.002 -dynamicoffsetfile=modulatoroffset +offset=2.0 diff --git a/b200-2048.ini b/b200-2048.ini index 7742a1d..92fe43e 100644 --- a/b200-2048.ini +++ b/b200-2048.ini @@ -67,8 +67,4 @@ behaviour_refclk_lock_lost=crash  [delaymanagement]  synchronous=0 -; choose between fixed and dynamic offset definition -management=dynamic - -fixedoffset=0.002 -dynamicoffsetfile=modulatoroffset +offset=0.002 diff --git a/b200-30.72.ini b/b200-30.72.ini index a66fbfe..be0e204 100644 --- a/b200-30.72.ini +++ b/b200-30.72.ini @@ -12,13 +12,13 @@ filename=/dev/stderr  [input]  ;transport=file -;source=/dev/stdin -loop=0 +;source=/home/bram/dab/mmbtools-aux/eti/AnnouncementTest-FraunhoferIIS-2013-11-25.eti +;loop=1  transport=zeromq  ;source=tcp://localhost:9100  source=tcp://core.mpb.li:9100 -max_frames_queued=400 +max_frames_queued=800  [modulator]  ; Gain mode: 0=FIX, 1=MAX, 2=VAR @@ -26,12 +26,12 @@ gainmode=2  ; Transmission mode  ; If not defined, take the mode from ETI -;mode=2 +mode=1  ; Set to 0 to disable CicEqualiser  dac_clk_rate=0 -digital_gain=1.0 +digital_gain=0.8  ; Output sample rate  rate=2048000 @@ -53,12 +53,12 @@ filename=/dev/null  device=  master_clock_rate=32768000  type=b200 -txgain=50 +txgain=40  ;frequency=234208000  channel=13C  ; possible values : internal, external, MIMO -refclk_source=gpsdo +refclk_source=gpsdo-ettus  ; possible values : none, external, MIMO  pps_source=gpsdo @@ -67,12 +67,15 @@ pps_source=gpsdo  ; possible values: ignore, crash  behaviour_refclk_lock_lost=crash +max_gps_holdover_time=600 +  ; Used for SFN with the UHD output  [delaymanagement]  synchronous=1 -; choose between fixed and dynamic offset definition -management=dynamic +offset=2.0 -fixedoffset=0.002 -dynamicoffsetfile=modulatoroffset +[tii] +enable = 0 +comb = 16 +pattern = 5 diff --git a/b200-resample.ini b/b200-resample.ini index ad72801..c537f1c 100644 --- a/b200-resample.ini +++ b/b200-resample.ini @@ -67,8 +67,4 @@ behaviour_refclk_lock_lost=crash  [delaymanagement]  synchronous=0 -; choose between fixed and dynamic offset definition -management=dynamic - -fixedoffset=0.002 -dynamicoffsetfile=modulatoroffset +offset=2.0 diff --git a/b200-sync-zmq.ini b/b200-sync-zmq.ini new file mode 100644 index 0000000..60f6611 --- /dev/null +++ b/b200-sync-zmq.ini @@ -0,0 +1,75 @@ +; Sample configuration file for ODR-DabMod + +[remotecontrol] +telnet=1 +telnetport=2121 + +[log] +syslog=0 +filelog=0 +filename=/dev/stderr + +[input] +;transport=file +;source=/dev/stdin +transport=zeromq +source=tcp://core.mpb.li:9100 +loop=0 + +[modulator] +; Gain mode: 0=FIX, 1=MAX, 2=VAR +gainmode=2 + +; Transmission mode +; If not defined, take the mode from ETI +mode=1 + +; Set to 0 to disable CicEqualiser +dac_clk_rate=0 + +digital_gain=0.8 + +; Output sample rate +rate=2048000 + +[firfilter] +enabled=1 +filtertapsfile=filter/simplefiltertaps.txt + +[output] +; choose output: possible values: uhd, file +output=uhd + +[fileoutput] +filename=/dev/null + +[uhdoutput] +;device=master_clock_rate=32768000,type=b100 +;txgain=2 +device= +master_clock_rate=32768000 +type=b200 +txgain=45 +;frequency=234208000 +channel=13C + +; possible values : internal, external, MIMO +refclk_source=internal + +; possible values : none, external, MIMO +pps_source=none + +; behaviour when external clock reference lock lost +; possible values: ignore, crash +behaviour_refclk_lock_lost=crash + +; Used for SFN with the UHD output +[delaymanagement] +synchronous=1 + +offset=2.0 + +[tii] +enable = 1 +comb = 16 +pattern = 6 diff --git a/b200-sync.ini b/b200-sync.ini index 6a996d5..b232f36 100644 --- a/b200-sync.ini +++ b/b200-sync.ini @@ -67,8 +67,4 @@ behaviour_refclk_lock_lost=crash  [delaymanagement]  synchronous=1 -; choose between fixed and dynamic offset definition -management=dynamic - -fixedoffset=0.002 -dynamicoffsetfile=modulatoroffset +offset=2.0 diff --git a/edi-test.mux b/edi-test.mux new file mode 100644 index 0000000..6d835b7 --- /dev/null +++ b/edi-test.mux @@ -0,0 +1,163 @@ +general { +    ; the DAB Transmission mode (values 1-4 accepted) +    dabmode 1 + +    ; the number of ETI frames to generate (set to 0 to get an unlimited number) +    nbframes 0 + +    ; The statsserver for extracting statistics +    statsserverport 12720 + +    syslog false +    writescca false +    tist false + +    new_fig_carousel true +} + +remotecontrol { +    ; enable the remote control server +    telnetport 12721 +} + +; Some ensemble parameters +ensemble { +    id 20479 +    ; Extended Country Code (decimal) +    ecc 225 + +    local-time-offset 2 +    international-table 1 +    label "TuxMux" +    shortlabel "Tux" +} + +services { +    funk { +        label "funk" +        shortlabel "funk" +        pty 0 +        language 0 +        id 10 +    } + +    funk2 { +        label "funk2" +        shortlabel "funk2" +        pty 0 +        language 0 +        id 11 +    } +    funk3 { +        label "funk3" +        shortlabel "funk3" +        pty 1 +        language 0 +        id 12 +    } + +} + +subchannels { +    funk { +        type audio +        inputfile "funk.mp2" +        nonblock false +        bitrate 128 +        id 10 +        protection 5 +    } + +    funk2 { +        type dabplus +        inputfile "funk2.dabp" +        nonblock false +        bitrate 96 +        id 11 +        protection 3 +    } + +    funk3 { +        type audio +        inputfile "funk.mp2" +        nonblock false +        bitrate 128 +        id 12 +        protection 3 +    } +} + +; For now, each component links one service to one subchannel +components { +    ; the component unique identifiers are not used anywhere, but +    ; are useful to disambiguate different components. +    funky { +        label funk +        shortlabel fu +        service funk +        subchannel funk +    } + +    funky2 { +        label funk2 +        shortlabel funk2 +        service funk2 +        subchannel funk2 +    } + +    funky3 { +        label funk3 +        shortlabel funk3 +        service funk3 +        subchannel funk3 +    } +} + +; A list of outputs, in the format +; unique_id "uri" +outputs { +    ;foobar "fifo:///home/bram/dab/mmbtools-aux/eti/funk2.10000.eti?type=raw" +    simul "simul://" +    null "fifo:///dev/null" + +    ; ZeroMQ output example +    ; zmq  "zmq+tcp://*:8080" + +    edi { + +        fec         2 +        ;chunk_len   140 + +        destinations { +            unicast { +                destination "10.31.0.120" +                ;destination "239.20.64.1" +                ;source      "192.168.2.10" +                sourceport  15321 +            } +            un_autre { +                destination "239.20.64.1" +                source      "10.31.0.121" +                sourceport  15322 +                ttl         2 +            } +        } + +        port        12002 + +        ; EDI uses the UDP protocol + +        ; Enable the PFT subsystem. If false, AFPackets are sent. +        enable_pft  true + +        ; Save the packets sent over ethernet to the file ./edi.debug +        dump        false + +        ; show more debugging info +        verbose     false + +        ; optional: what kind of alignment to do in the tagpacket +        ;tagpacket_alignment 16 +    } + +} diff --git a/edi/edidebug.py b/edi/edidebug.py index 64d605d..c41d9a6 100755 --- a/edi/edidebug.py +++ b/edi/edidebug.py @@ -482,7 +482,8 @@ def tagitems(tagpacket):          name, length = struct.unpack(tag_item_head_struct, tagpacket[i:i+8])          # length is in bits, because it's more annoying this way -        assert(length % 8 == 0) +        if length % 8 != 0: +            print("ASSERTION ERROR: length of tagpacket is not multiple of 8: {}".format(length))          length /= 8          tag_value = tagpacket[i+8:i+8+length] diff --git a/edi/edisend.py b/edi/edisend.py new file mode 100755 index 0000000..2bd6bc1 --- /dev/null +++ b/edi/edisend.py @@ -0,0 +1,193 @@ +#!/usr/bin/env python2 +# +# Read an EDI dump file and transmit over UDP +# +# The MIT License (MIT) +# +# Copyright (c) 2015 Matthias P. Braendli +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +#    The above copyright notice and this permission notice shall be included in +#    all copies or substantial portions of the Software. +# +#    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +#    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +#    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +#    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +#    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +#    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +#    THE SOFTWARE. + +import sys +import struct + +from crc import crc16 +from reedsolo import RSCodec + +import socket +import time + +UDP_IP = "239.20.64.1" +UDP_PORT = 12002 + +class BufferedFile: +    def __init__(self, fname): +        self.buf = [] + +        if fname == "-": +            self.fd = sys.stdin +        else: +            self.fd = open(fname, "rb") + +    def read(self, n): +        if not self.buf: +            return self.fd.read(n) +        else: +            if len(self.buf) < n: +                self.buf.extend(self.fd.read(n - len(self.buf))) + +            if len(self.buf) == n: +                ret = b"".join(self.buf) +                self.buf = [] +            else: +                ret = b"".join(self.buf[:n]) +                del self.buf[:n] + +            return ret + +    def peek(self, n): +        dat = self.fd.read(n) +        self.buf.extend(dat) +        return dat + + +pft_head_struct = "!2sH3B3BH" +pft_rs_head_struct = "!2B" +pft_addr_head_struct = "!2H" +af_head_struct = "!2sLHBc" +class EDI: +    def __init__(self): +        self.last_send_time = time.time() +        self.sock = socket.socket(socket.AF_INET, # Internet +                         socket.SOCK_DGRAM) # UDP + +    def send_udp(self, message): +        self.sock.sendto(message, (UDP_IP, UDP_PORT)) + + +    def decode(self, stream): +        sync = stream.peek(2) + +        if len(sync) < 2: +            return False + +        if sync == "PF": +            return self.decode_pft(stream) +        elif sync == "AF": +            return self.decode_af(stream, is_stream=True) + + + +    def decode_pft(self, stream): +        headerdata = stream.read(12) +        header = struct.unpack(pft_head_struct, headerdata) + +        psync, pseq, findex1, findex2, findex3, fcount1, fcount2, fcount3, fec_ad_plen = header + +        findex = (findex1 << 16) | (findex2 << 8) | findex3 +        fcount = (fcount1 << 16) | (fcount2 << 8) | fcount3 + +        fec = (fec_ad_plen & 0x8000) != 0x00 +        addr = (fec_ad_plen & 0x4000) != 0x00 +        plen = fec_ad_plen & 0x3FFF + +        rs_k = 0 +        rs_z = 0 +        if fec: +            rs_head = stream.read(2) +            rs_k, rs_z = struct.unpack(pft_rs_head_struct, rs_head) +            headerdata += rs_head + +        addr_source = 0 +        addr_dest   = 0 +        if addr: +            addr_head = stream.read(4) +            addr_source, addr_dest = struct.unpack(pft_addr_head_struct, addr_head) +            headerdata += addr_head + +        # read CRC +        crc_data = stream.read(2) +        crc = struct.unpack("!H", crc_data)[0] + +        crc_calc = crc16(headerdata) +        crc_calc ^= 0xFFFF + +        crc_ok = crc_calc == crc + +        time_now = time.time() +        if findex == 0: +            if self.last_send_time + 24e-3 > time_now: +                delay = self.last_send_time + 24e-3 - time_now +                print("Sleeping for {} ms".format(1000 * delay)) +                time.sleep(delay) +            self.last_send_time = time_now + + +        if crc_ok: +            payload = stream.read(plen) +            self.send_udp(headerdata + crc_data + payload) + +        return crc_ok + + +    def decode_af(self, in_data, is_stream=False): +        if is_stream: +            headerdata = in_data.read(10) +        else: +            headerdata = in_data[:10] + +        sync, plen, seq, ar, pt = struct.unpack(af_head_struct, headerdata) + +        if sync != "AF": +            return False + +        crc_flag = (ar & 0x80) != 0x00 +        revision = ar & 0x7F + +        if is_stream: +            payload = in_data.read(plen) +            crc_data = in_data.read(2) +            crc = struct.unpack("!H", crc_data)[0] +        else: +            payload = in_data[10:10+plen] +            crc_data = in_data[10+plen:10+plen+2] +            crc = struct.unpack("!H", crc_data)[0] + +        crc_calc = crc16(headerdata) +        crc_calc = crc16(payload, crc_calc) +        crc_calc ^= 0xFFFF + +        crc_ok = crc_calc == crc + +        if crc_ok: +            self.send_udp(headerdata + payload + crc_data) + +        return crc_ok + +if len(sys.argv) > 1: +    filename = sys.argv[1] + +    edi_fd = BufferedFile(filename) +else: +    edi_fd = BufferedFile("-") + +edi = EDI() +while edi.decode(edi_fd): +    pass + diff --git a/encode-alsa32.sh b/encode-alsa32.sh deleted file mode 100755 index 750ecc5..0000000 --- a/encode-alsa32.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash -# -# Read audio from ALSA input using sox, and encode with fdk-aac-dabplus-zmq -# -BITRATE=$1 -DST=$2 -ALSASRC="default" - -if [ "$DST" == "" ] -then -    echo "Usage:" -    echo " $0 <bitrate> <zmq destination>" -    exit 1 -fi - -dabplus-enc -d $ALSASRC -c 2 -r 32000 -b $BITRATE -o $DST -p 48 - diff --git a/encode-alsasrc-gst-dabplus.sh b/encode-alsasrc-gst-dabplus.sh deleted file mode 100755 index 7ec5d9e..0000000 --- a/encode-alsasrc-gst-dabplus.sh +++ /dev/null @@ -1,39 +0,0 @@ -#!/bin/bash -# -# Read audio from ALSA input using gstreamer, and encode with fdk-aac-dabplus-zmq -# -BITRATE=$1 -DST=$2 -QUEUEDELAY=400000 #400ms - -GSTREAMER_VERSION="0" - -if [ "$DST" == "" ] -then -    echo "Usage:" -    echo " $0 <bitrate> <zmq destination>" -    exit 1 -fi - - -if [ "$GSTREAMER_VERSION" == "1" ] -then -    gst-launch-1.0 -q \ -        alsasrc "device=default" ! \ -        audio/x-raw, 'rate=48000,format=S16LE,channels=2' ! \ -        queue "max-size-time=$QUEUEDELAY" ! \ -        filesink location="/dev/stdout" | \ -        dabplus-enc \ -            -i /dev/stdin -b $BITRATE -f raw -a -o "${DST}" - -elif [ "$GSTREAMER_VERSION" == "0" ] -then -    gst-launch -q \ -        alsasrc "device=default" ! \ -        audio/x-raw-int, 'rate=48000,format=S16LE,channels=2' ! \ -        queue "max-size-time=$QUEUEDELAY" ! \ -        filesink location="/dev/stdout" | \ -        dabplus-enc \ -            -i /dev/stdin -b $BITRATE -f raw -a -o "${DST}" -fi - diff --git a/encode-alsasrc-sox-mpeg.sh b/encode-alsasrc-sox-mpeg.sh deleted file mode 100755 index 3de71b5..0000000 --- a/encode-alsasrc-sox-mpeg.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash -# -# Read audio from ALSA input using sox, and encode with toolame, -# send to ZMQ -# -# This needs toolame-dab from -# https://github.com/Opendigitalradio/toolame-dab -# -BITRATE=$1 -DST=$2 -ALSASRC="default" - -if [ "$DST" == "" ] -then -    echo "Usage:" -    echo " $0 <bitrate> <zmq destination>" -    exit 1 -fi - -sox -t alsa $ALSASRC -b 16 -t raw - rate 48k channels 2 | \ -    toolame -s 48 -D 4 -b $BITRATE /dev/stdin $DST - diff --git a/encode-fb.sh b/encode-fb.sh deleted file mode 100755 index f97f3a3..0000000 --- a/encode-fb.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash -echo "Encoding FB to 1.ff" -mpg123 -r 48000 -s http://fbpc5.epfl.ch:8001 |toolame -s 48 -D 4 -b 128 /dev/stdin ./1.ff diff --git a/encode-fbplus.sh b/encode-fbplus.sh deleted file mode 100755 index 1e1bfc8..0000000 --- a/encode-fbplus.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/bash -# -# Encode Frequence Banane to ZMQ -# -# Remarks: it's probably better to use the -# snd-aloop scenario now. - -WITH_GSTREAMER=0 -URL=http://fbpc5.epfl.ch:8000/fb_192 -BITRATE=$1 -DST=$2 - -if [ "$DST" == "" ] -then -    echo "Usage:" -    echo " $0 <bitrate> <zmq destination>" -    exit 1 -fi - -if [ "$WITH_GSTREAMER" == "1" ] -then -    gst-launch-0.10 -q \ -        uridecodebin uri=$URL ! \ -        queue ! \ -        audioresample quality=8 ! \ -        audioconvert ! \ -        audio/x-raw-int, 'rate=48000,format=S16LE,channels=2' ! \ -        filesink location="/dev/stdout" | \ -        dabplus-enc \ -            -i /dev/stdin -b $BITRATE -f raw -a -o $DST -else -    mpg123 -s $URL |\ -        sox -t raw -r 44100 -e signed -b 16 -c 2 -   -t raw  - rate 32k |\ -        dabplus-enc \ -            -i /dev/stdin -r 32000 -b $BITRATE -f raw -a -o $DST -fi diff --git a/encode-jack.sh b/encode-jack.sh deleted file mode 100755 index ab7e9aa..0000000 --- a/encode-jack.sh +++ /dev/null @@ -1,154 +0,0 @@ -#!/bin/bash -# -# mplayer - jack - jack-stdout - dabplus-enc -# encoder script. -# -# Used to encode webstreams for DAB+, requires -# jackd -d dummy -r 32000 -# to be running - -printerr() { -    echo -e "\033[01;31m$1\033[0m" -} - -printmsg() { -    echo -e "\033[01;32m$1\033[0m" -} - -set -u - -# check number of arguments -if [[ "$#" < 3 ]] ; then -    echo "Usage $0 url jack-id destination [volume]" -    echo "The volume setting is optional" -    exit 1 -fi - -if [[ "$#" > 2 ]] ; then -    URL=$1 -    ID=$2 -    DST=$3 -fi - -if [[ "$#" == 4 ]] ; then -    VOL=$4 -else -    VOL="" -fi - -BITRATE=80 -RATE=32000 - -encoderalive=0 -mplayerpid=0 -encoderpid=0 - -# The trap for Ctrl-C -sigint_trap() { -    printerr "Got Ctrl-C, killing mplayer and encoder" - -    if [[ "$mplayerpid" != "0" ]] ; then -        kill -TERM $mplayerpid -        sleep 2 -        kill -KILL $mplayerpid -    fi - -    if [[ "$encoderpid" != "0" ]] ; then -        kill -TERM $encoderpid -        sleep 2 -        kill -KILL $encoderpid -    fi - -    printmsg "Goodbye" -    exit -} - -trap sigint_trap SIGINT - -while true -do -    mplayer_ok=0 - -    if [[ "$mplayerpid" == "0" ]] ; then -        if [[ "$VOL" == "" ]] ; then -            mplayer -quiet -af resample=$RATE:0:2 -ao jack:name=$ID $URL & -            mplayerpid=$! -        else -            mplayer -quiet -af resample=$RATE:0:2 -af volume=$VOL -ao jack:name=$ID $URL & -            mplayerpid=$! -        fi - -        printmsg "Started mplayer with pid $mplayerpid" - -        # give some time to mplayer to set up and -        # wait until port becomes visible -        timeout=10 - -        while [[ "$mplayer_ok" == "0" ]] -        do -            printmsg "Waiting for mplayer to connect to jack ($timeout)" -            sleep 1 -            mplayer_ok=$(jack_lsp $ID:out_0 | wc -l) - -            timeout=$(( $timeout - 1)) - -            if [[ "$timeout" == "0" ]] ; then -                printerr "mplayer doesn't connect to jack !" -                kill $mplayerpid -                break -            fi -        done -    else -        printmsg "No need to start mplayer: $mplayerpid" -    fi - -    if [[ "$mplayer_ok" == "1" ]] ; then -        jack-stdout $ID:out_0 $ID:out_1 | \ -            dabplus-enc -i /dev/stdin -l \ -            -b $BITRATE -r $RATE -f raw -a -o $DST & -        encoderpid=$! -    fi - -    printmsg "Started encoder with pid $encoderpid" - -    sleep 5 - -    checkloop=1 -    while [[ "$checkloop" == "1" ]] -    do -        sleep 2 - -        kill -s 0 $mplayerpid -        if [[ "$?" != "0" ]] ; then -            # mplayer died -            # we must kill jack-stdout, because we cannot reconnect it -            # to a new mplayer, since we do not know the jack-stdout name. -            # And it has no cmdline option to set one, Rrrrongntudtjuuu! -            if [[ "$encoderpid" != "0" ]] ; then -                kill -TERM $encoderpid -            fi -            checkloop=0 - -            # mark as dead -            mplayerpid=0 - -            printerr "Mplayer died" -        fi - -        if [[ "$encoderpid" != "0" ]] ; then -            kill -s 0 $encoderpid -            if [[ "$?" != "0" ]] ; then -                # the encoder died, -                # no need to kill the mplayer, we can reconnect to it - -                checkloop=0 - -                printerr "Encoder died" -            fi -        fi -    done - -    sleep 5 - -done - diff --git a/encode-stdin.sh b/encode-stdin.sh deleted file mode 100755 index c97a5ce..0000000 --- a/encode-stdin.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -toolame -s 48 -D 4 -b 128 /dev/stdin /dev/stdout > 1.ff diff --git a/encode-url-gst-dabplus.sh b/encode-url-gst-dabplus.sh deleted file mode 100755 index b05d7bb..0000000 --- a/encode-url-gst-dabplus.sh +++ /dev/null @@ -1,44 +0,0 @@ -#!/bin/bash -# -# Read a URL using gstreamer, and encode with fdk-aac-dabplus-enc -# -URL=$1 -BITRATE=$2 -DST=$3 -QUEUEDELAY=400000 #400ms - -GSTREAMER_VERSION="1" - -if [ "$DST" == "" ] -then -    echo "Usage:" -    echo " $0 <url> <bitrate> <zmq destination>" -    exit 1 -fi - - -if [ "$GSTREAMER_VERSION" == "1" ] -then -    gst-launch-1.0 -q \ -        uridecodebin uri=$URL ! \ -        queue "max-size-time=$QUEUEDELAY" ! \ -        audioresample quality=8 ! \ -        audioconvert ! \ -        audio/x-raw, 'rate=48000,format=S16LE,channels=2' ! \ -        filesink location="/dev/stdout" | \ -        dabplus-enc \ -            -i /dev/stdin -b $BITRATE -f raw -a -o "${DST}" - -elif [ "$GSTREAMER_VERSION" == "0" ] -then -    gst-launch -q \ -        uridecodebin uri=$URL ! \ -        queue "max-size-time=$QUEUEDELAY" ! \ -        audioresample quality=8 ! \ -        audioconvert ! \ -        audio/x-raw-int, 'rate=48000,format=S16LE,channels=2' ! \ -        filesink location="/dev/stdout" | \ -        dabplus-enc \ -            -i /dev/stdin -b $BITRATE -f raw -a -o "${DST}" -fi - diff --git a/histogram.py b/histogram.py new file mode 100755 index 0000000..c17a255 --- /dev/null +++ b/histogram.py @@ -0,0 +1,28 @@ +#!/usr/bin/python2 +# +# Print sample histograms from dabmod I/Q file + +import sys + +import pylab as P +import numpy + + +fd = open('test.iq', 'rb') + +read_data = numpy.fromfile(file=fd, dtype=numpy.float32, count=-1) + +print("MAX absolute value: {}".format(numpy.max(numpy.abs(read_data)))) + +if 0: +    P.plot(read_data) + +if 1: +    P.figure() + +    # the histogram of the data with histtype='step' +    n, bins, patches = P.hist(read_data, 50, normed=1, histtype='stepfilled') +    P.setp(patches, 'facecolor', 'g', 'alpha', 0.75) + +P.show() + diff --git a/minimal.ini b/minimal.ini new file mode 100644 index 0000000..8f98890 --- /dev/null +++ b/minimal.ini @@ -0,0 +1,11 @@ +[input] +transport=zeromq +source=tcp://core.mpb.li:9100 +max_frames_queued=400 + +[output] +output=file + +[fileoutput] +filename=/dev/null + diff --git a/modulatoroffset b/modulatoroffset index 2348025..5186d07 100644 --- a/modulatoroffset +++ b/modulatoroffset @@ -1 +1 @@ -1.200151 +4.0 diff --git a/mpeg_analyse.py b/mpeg_analyse.py new file mode 100755 index 0000000..495e808 --- /dev/null +++ b/mpeg_analyse.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python2 +# +# Analyse an mp2 file for debugging +# +# The MIT License (MIT) +# +# Copyright (c) 2016 Matthias P. Braendli +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +#    The above copyright notice and this permission notice shall be included in +#    all copies or substantial portions of the Software. +# +#    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +#    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +#    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +#    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +#    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +#    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +#    THE SOFTWARE. + +from pprint import pprint +import sys +import struct + +# Header: +# AAAAAAAA AAABBCCD EEEEFFGH IIJJKLMM +# see http://mpgedit.org/mpgedit/mpeg_format/mpeghdr.htm + +audio_version_ids = ["MPEG version 2.5", "reserved", "MPEG Version 2 (ISO/IEC 13818-3)", "MPEG Version 1 (ISO/IEC 11172-3)"] +layer_descriptions = ["reserved", "Layer III", "Layer II", "Layer I"] + +# bitrate index: +# bits	V1,L1	V1,L2	V1,L3	V2,L1	V2, L2 & L3 +# 0000	free	free	free	free	free +# 0001	32	32	32	32	8 +# 0010	64	48	40	48	16 +# 0011	96	56	48	56	24 +# 0100	128	64	56	64	32 +# 0101	160	80	64	80	40 +# 0110	192	96	80	96	48 +# 0111	224	112	96	112	56 +# 1000	256	128	112	128	64 +# 1001	288	160	128	144	80 +# 1010	320	192	160	160	96 +# 1011	352	224	192	176	112 +# 1100	384	256	224	192	128 +# 1101	416	320	256	224	144 +# 1110	448	384	320	256	160 +# 1111	bad	bad	bad	bad	bad + +def analyse(fd): +    headerdata = struct.unpack("!BBBB", fd.read(4)) + +    sync = (headerdata[0] << 3) | (headerdata[1] >> 5) +    print("Sync: {:x}".format(sync)) + +    audio_version_id = (headerdata[1] >> 3) & 0x3 +    print("audio_version_id {}".format(audio_version_ids[audio_version_id])) + +    layer_description = (headerdata[1] >> 1) & 0x3 +    print("layer_description {}".format(layer_descriptions[layer_description])) + +    protection_bit = headerdata[1] & 0x1 +    if protection_bit: +        print("Without protection") +    else: +        print("With protection") + +    bitrate_index = headerdata[2] >> 4 +    print("bitrate_index {}".format(bitrate_index)) + +    sampling_rate = (headerdata[2] >> 2) & 0x3 +    print("sampling_rate {}".format(sampling_rate)) + +    padding_bit = (headerdata[2] >> 1) & 0x1 +    if padding_bit: +        print("With padding") +    else: +        print("Without padding") + +    private_bit = headerdata[2] & 0x1 +    if private_bit: +        print("Private bit set") +    else: +        print("Private bit unset") + +    channel_mode = headerdata[3] >> 6 +    print("channel_mode {}".format(channel_mode)) + +    mode_extension = (headerdata[3] >> 4) & 0x3 +    print("mode_extension {}".format(mode_extension)) + +    copyright = (headerdata[3] >> 3) & 0x1 +    if copyright: +        print("With copyright") +    else: +        print("Without copyright") + +    original = (headerdata[3] >> 2) & 0x1 +    if original: +        print("With original") +    else: +        print("Without original") + +    emphasis = headerdata[3] & 0x3 +    print("emphasis {}".format(emphasis)) + + + +fd = open(sys.argv[1], "rb") +analyse(fd) diff --git a/rdsparse/decoder_impl.cc b/rdsparse/decoder_impl.cc index 2bfa5a4..625a520 100644 --- a/rdsparse/decoder_impl.cc +++ b/rdsparse/decoder_impl.cc @@ -101,14 +101,24 @@ int decoder_impl::work (int noutput_items,                              lastseen_offset=j;                              lastseen_offset_counter=bit_counter;                              presync=true; +                            lout << "@@@@@ presync" << std::endl;                          }                          else {                              bit_distance=bit_counter-lastseen_offset_counter; -                            if (offset_pos[lastseen_offset]>=offset_pos[j])  +                            if (offset_pos[lastseen_offset]>=offset_pos[j]) {                                  block_distance=offset_pos[j]+4-offset_pos[lastseen_offset]; -                            else +                            } +                            else {                                  block_distance=offset_pos[j]-offset_pos[lastseen_offset]; -                            if ((block_distance*26)!=bit_distance) presync=false; +                            } + +                            if ((block_distance*26)!=bit_distance) { +                                presync=false; +                                lout << "@@@@@ presync lost " << j << " " << +                                   block_distance*26 << " " << +                                   bit_distance << " " << +                                   std::endl; +                            }                              else {                                  lout << "@@@@@ Sync State Detected" << std::endl;                                  enter_sync(j); @@ -148,6 +158,11 @@ int decoder_impl::work (int noutput_items,                          else {                              wrong_blocks_counter++;                              good_block=false; +                            lout << "@@@@@ CRC " << +                              std::hex << std::setfill('0') << std::setw(4) << +                              block_received_crc << " " << +                              std::hex << std::setfill('0') << std::setw(4) << +                              block_calculated_crc << std::dec << std::endl;                          }                      }  /* done checking CRC */ @@ -1,7 +1,7 @@  ; Sample configuration file for CRC-DABMOD  [remotecontrol] -telnet=1 +telnet=0  telnetport=2121  [log] @@ -11,12 +11,13 @@ filename=/dev/stderr  [input]  transport=file -;source=/home/bram/dab/mmbtools-aux/eti/csp.eti -source=/dev/stdin +source=/home/bram/dab/mmbtools-aux/eti/funk2.100.eti +;source=/dev/stdin +loop=0  ;transport=zeromq -;source=tcp://localhost:8080 -loop=0 +;source=tcp://localhost:9100 +;max_frames_queued=400  [modulator]  ; Gain mode: 0=FIX, 1=MAX, 2=VAR @@ -24,7 +25,7 @@ gainmode=2  ; Transmission mode  ; If not defined, take the mode from ETI -mode=1 +;mode=0  ; Set to 0 to disable CicEqualiser  dac_clk_rate=0 @@ -32,25 +33,30 @@ dac_clk_rate=0  digital_gain=1.0  ; Output sample rate -rate=8000000 +rate=2048000  [firfilter] -enabled=1 +enabled=0  filtertapsfile=filter/simplefiltertaps.txt  [output]  ; choose output: possible values: uhd, file  output=file +[zmqoutput] +listen=tcp://*:9200 +socket_type=rep +  [fileoutput] -filename=/dev/stdout +filename=ofdm.iq +;filename=/dev/null  [uhdoutput]  ;frequency=234208000  channel=13C  ; For the B200 -master_clock_rate=32000000 +master_clock_rate=32768000  type=b200  txgain=35 @@ -73,7 +79,4 @@ behaviour_refclk_lock_lost=crash  synchronous=0  ; choose between fixed and dynamic offset definition -management=dynamic - -fixedoffset=0.002 -dynamicoffsetfile=modulatoroffset +offset=0.002 diff --git a/zmq-simul.mux b/zmq-simul.mux index 5689f31..6a599dc 100644 --- a/zmq-simul.mux +++ b/zmq-simul.mux @@ -8,9 +8,11 @@ general {      ; The statsserver for extracting statistics      statsserverport 12720 -    syslog false +    syslog true      writescca false      tist true + +    new_fig_carousel true  }  remotecontrol { @@ -29,6 +31,26 @@ ensemble {      international-table 1      label "TuxMux"      shortlabel "Tux" + +    ; Announcement settings for FIG0/19 +    announcements { +        alarm { +            cluster 0xFF +            flags { +                Alarm true +            } + +            subchannel sub-fb +        } +        test_announcement { +            cluster 1 +            flags { +                Traffic true +            } + +            subchannel sub-fb +        } +    }  }  services { @@ -39,11 +61,23 @@ services {          language 0          id 0x4060          ; also supports id + +        announcements { +            Traffic true +            Alarm true +            clusters "1,255" +        }      }      srv-label {          label "label"          id 0x4040          ; also supports id + +        announcements { +            Traffic true +            Alarm true +            clusters "1,255" +        }      }  } @@ -54,10 +88,11 @@ subchannels {      sub-fb {          type dabplus          ; use ZeroMQ: -        inputfile "tcp://*:9001" +        ;inputfile "tcp://*:9001" +        inputfile "./stream-0.msc"          zmq-buffer 40          zmq-prebuffering 20 -        bitrate 80 +        bitrate 128          id 24          protection 3 @@ -68,13 +103,21 @@ subchannels {      }      sub-label {          type audio -        inputfile "tcp://*:9002" +        inputfile "/home/bram/dab/mmbtools-aux/fip-j-ok.mp2" +        ;inputfile "tcp://*:9002"          zmq-buffer 40          zmq-prebuffering 20          bitrate 128          id 4          protection 3      } +;    sub-data { +;       type data +;       inputfile "udp://:9003" +;       bitrate 16 +;       id 5 +;       protection 3 +;   }  }  ; For now, each component links one service to one subchannel @@ -91,7 +134,6 @@ components {      }      comp-label { -        label "label"          service srv-label          subchannel sub-label      } @@ -100,10 +142,48 @@ components {  ; A list of outputs, in the format  ; unique-id "uri"  outputs { -    stdout "fifo:///dev/stdout?type=raw" -    zmq  "zmq+tcp://*:9100" +    ;stdout "fifo:///dev/stdout?type=raw" +    ;thefile "file://./zmq-simul.eti?type=raw" +    ;zmq  "zmq+tcp://*:9100"      ; This throttles muxing down to nominal rate      throttle "simul://" + +    ;net_udp "udp://shack.local:31000" +    net_tcp "tcp://0.0.0.0:31000" + +    edi { +        fec         0 +        chunk_len   200 + +        destinations { +            multicast { +                destination "239.20.64.1" +                ;source      "192.168.2.10" +                source      "192.168.0.100" +                sourceport  52321 +                ttl 1 +            } +            ;unicast { +                ;destination "192.168.2.2" +            ;} +        } + +        port        52002 + +        ; EDI uses the UDP protocol + +        ; Enable the PFT subsystem. If false, AFPackets are sent. +        enable_pft  true + +        ; Save the packets sent over ethernet to the file ./edi.debug +        dump        false + +        ; show more debugging info +        verbose     false + +        ; optional: what kind of alignment to do in the tagpacket +        tagpacket_alignment 16 +    }  } | 
