portapack-mayhem / mayhem-firmware

Custom firmware for the HackRF+PortaPack H1/H2/H4
https://hackrf.app
GNU General Public License v3.0
3.5k stars 564 forks source link

simple discriminator output tap for decoding Digital Mobile Radio #1678

Open RE-Specto opened 10 months ago

RE-Specto commented 10 months ago

currently, it is impossible to decode any digital voice using the portapack. there is https://github.com/eried/portapack-mayhem/issues/360 asking for such support to be added (which I highly admire) however a much easier and quicker solution would be just to route the audio out to a raspberry pi running DSD, which is impossible at the moment however due to permanently enabled audio filter in the portapack.

Describe the solution you'd like basically an option to output the raw unfiltered audio, also known as "discriminator tap", similar to removing the "filter audio" checkbox in this SDRSharp screenshot: image

an alternative would be to implement proper digital radio decoding natively https://github.com/eried/portapack-mayhem/issues/360 which would be much better solution, however way harder to implement as well.

gullradriel commented 10 months ago

Hmm

I don't know anything about what you're asking, but peeking at the code I see this:

void AudioOutput::fill_audio_buffer(const buffer_f32_t& audio, const bool send_to_fifo) {
    std::array<int16_t, 32> audio_int;

    auto audio_buffer = audio::dma::tx_empty_buffer();
    for (size_t i = 0; i < audio_buffer.count; i++) {
        const int32_t sample_int = audio.p[i] * k;
        const int32_t sample_saturated = __SSAT(sample_int, 16);
        audio_buffer.p[i].left = audio_buffer.p[i].right = sample_saturated;
        audio_int[i] = sample_saturated;
    }
    if (stream && send_to_fifo) {
        stream->write(audio_int.data(), audio_buffer.count * sizeof(audio_int[0]));
    }
    feed_audio_stats(audio);
}

As you can see audio values seems to be somewhat filled here with the help of __SSAT. Is it something like this you want to deactivate ?

Is looking at audio configure ringing a bell ?

void AudioOutput::configure(
    const iir_biquad_config_t& hpf_config,
    const iir_biquad_config_t& deemph_config,
    const float squelch_threshold) {
    hpf.configure(hpf_config);
    deemph.configure(deemph_config);
    squelch.set_threshold(squelch_threshold);
}
RE-Specto commented 10 months ago

hmm.. not sure about __SSAT, but audio configure gives some clues.. there is _deemphconfig which i believe is the De-Emphasis Low Pass Filter, which afaik is the evil for further recovering of a digital signal from the audio output. there's _hpfconfig as well which is probably a High Pass Filter, not sure if this one interferes with digital, but for an initial test it worth a try disabling both of them.

there's also some code for the microphone which employs filtering, not sure if and where the same thing is applied to audio output 🤔maybe it'll provide you with additional clues:

void AK4951::microphone_enable(int8_t alc_mode, bool mic_to_HP_enabled) {
    // alc_mode =0 = (OFF =same as original code = NOT using AK4951 Programmable digital filter block),
    // alc_mode >1 (with  DIGITAL FILTER BLOCK , example :  1:(+12dB) , 2:(+9dB)", 3:(+6dB), ...)
    if (alc_mode == 0) {  // Programmable Digital Filter OFF,  same as original condition., no Digital ALC, nor Wind Noise Filter, LPF , EQ

        map.r.digital_filter_select_2.LPF = 0;  // LPF-Block, Coeffic Setting Enable (OFF-Default), When LPF bit is “0”, audio data passes the LPF block by 0dB gain.
        update(Register::DigitalFilterSelect2);

        // Pre-loading AUDIO PATH with all DIGITAL BLOCK by pased, see, audio path block diagramm AK4951 datasheet + Table  Playback mode  -Recording mode.
        // Digital filter block PATH is BY PASSED (we can swith off DIG. BLOCK power , PMPFIL=0) .The Path in Recording Mode 2 & Playback Mode 2  (NO DIG FILTER BLOCK AT ALL, not for MIC recording, nor for Playback)
        map.r.digital_filter_mode.ADCPF = 1;     // ADCPF bit swith ("0" Mic  after ADC Output connected (recording mode) to the DIGITAL FILTER  BLOCK. ("1" Playback mode)
        map.r.digital_filter_mode.PFSDO = 0;     // ADC bit switch ("0" : 1st order HPF) connectedto the  Output. By bass DIGITAL block .
        map.r.digital_filter_mode.PFDAC = 0b00;  // (Input selector for DAC (not used in MIC), SDTI= Audio Serial Data Input Pin)
        update(Register::DigitalFilterMode);     // Writing the Audio Path :  NO DIGITAL BLOCK or DIG BLOCK FOR  MIC ,   Audio mode path : Playback mode /-Recording mode.

        map.r.power_management_1.PMADL = 1;   // ADC Lch = Lch input signal. Mic Amp Lch and ADC Lch Power Management
        map.r.power_management_1.PMADR = 0;   // ADC Rch = Rch input signal. Mic Amp Rch and ADC Rch Power Management. (PMADL=1, PMADR=0) means MONO MIC input connected to Left pin.
        map.r.power_management_1.PMPFIL = 0;  // Pre-loading , Programmable Dig. filter OFF ,filter unused, routed around.(original value = 0 )

also some explanation about FM "Filter Audio" that I found in a forum: https://groups.io/g/airspy/topic/sdr_filter_audio_setting/75729908

oh, and thanks for your swift reply 👍

htotoo commented 10 months ago

As I can recall, the audio out is application dependent. So while in an app there is filter A, on the other app, it has filter B. So if you tell hat app you wana use, there should be a checkbox to skip audio filter. (If it can handle it)

RE-Specto commented 10 months ago

well since the goal is being able to output Unfiltered "narrow FM audio" for further external decoding, so probably the "Receive/Audio" is the best suitable app for this feature? (unless it deserves a separate app?🤔)

if you can help providing some testing code, I'll flash it and do some testing to verify. if successful, i"ll document the steps for others to use as well.

htotoo commented 10 months ago

I think the best would be a stripped down external app for this task. Won't complicate the existing ones, and also no additional bug factor because of the big change.