From 5eeff10916086b10262cdc110e28759e11a8964b Mon Sep 17 00:00:00 2001 From: Jan Date: Thu, 2 Oct 2025 18:53:42 +0200 Subject: [PATCH] Ctrl-I key --- README.md | 2 +- app-files/appimage/udev-rules-helper | 19 +--- sources/frontend/fib-config.h | 18 +-- sources/frontend/fib-decoder.cpp | 160 ++++++++++++++------------- sources/frontend/fib-decoder.h | 13 ++- sources/frontend/ofdm-decoder.cpp | 94 ++++++++-------- sources/frontend/sample-reader.cpp | 3 +- sources/main/radio.cpp | 82 ++++++-------- 8 files changed, 188 insertions(+), 203 deletions(-) diff --git a/README.md b/README.md index 587b04ef..9a4c2979 100755 --- a/README.md +++ b/README.md @@ -531,7 +531,7 @@ the *qt-dab-6.9.pro* file contains (much) more configuration options than the *CMakeLists.txt* file that is used when using cmake. Note that the scheme presented below is applied when building the AppImage -on Ubuntu 20, and was tested on the "bullseye" system on the RPI. +on Ubuntu 22, and was tested on the "bullseye" system on the RPI. For other distributions (or later Ubuntu versions), names of library packages may be different. Note that in all cases, the development versions (i.e. the versions with the include (".h") files) are required. diff --git a/app-files/appimage/udev-rules-helper b/app-files/appimage/udev-rules-helper index ca1de666..fc046b67 100644 --- a/app-files/appimage/udev-rules-helper +++ b/app-files/appimage/udev-rules-helper @@ -4,25 +4,12 @@ HERE="$(dirname "$(readlink -f "${0}")")" rmmod dvb_usb_rtl28xxu || true -cat > /tmp/10-rtl-sdr.rules <<\EOF +cat > ./10-rtl-sdr.rules <<\EOF SUBSYSTEM=="usb", ATTRS{idVendor}=="0bda", ATTRS{idProduct}=="2832", MODE="0666", SYMLINK+="rtl_sdr" EOF -mv /tmp/10-rtl-sdr.rules /etc/udev/rules.d/10-rtl-sdr.rules - -cat > /tmp/52-airspy.rules <<\EOF -ATTR{idVendor}=="1d50", ATTR{idProduct}=="60a1", SYMLINK+="airspy-%k", MODE="660", GROUP="plugdev" -EOF - -mv /tmp/52-airspy.rules /etc/udev/rules.d/52-airspy.rules - -cat > /tmp/66-mirics.rules <<\EOF -SUBSYSTEM=="usb",ENV{DEVTYPE}=="usb_device",ATTRS{idVendor}=="1df7",ATTRS{idProduct}=="2500",MODE:="0666" -SUBSYSTEM=="usb",ENV{DEVTYPE}=="usb_device",ATTRS{idVendor}=="1df7",ATTRS{idProduct}=="3010",MODE:="0666" -SUBSYSTEM=="usb",ENV{DEVTYPE}=="usb_device",ATTRS{idVendor}=="1df7",ATTRS{idProduct}=="3000",MODE:="0666" -EOF - -mv /tmp/66-mirics.rules /etc/udev/rules.d/66-mirics.rules +mv ./10-rtl-sdr.rules /etc/udev/rules.d/10-rtl-sdr.rules udevadm control --reload-rules udevadm trigger --attr-match=subsystem=usb + diff --git a/sources/frontend/fib-config.h b/sources/frontend/fib-config.h index 5998a4ac..c9b2c558 100644 --- a/sources/frontend/fib-config.h +++ b/sources/frontend/fib-config.h @@ -121,15 +121,15 @@ public: } FIG18_cluster; // // for each type a table - std::vector SId_table; - std::vector subChannel_table; // FIG0/1 - std::vector SC_C_table; // FIG0/2 - std::vector SC_P_table; // FIG0/3 - std::vector SC_G_table; // FIG0/8 - std::vector language_table; // FIG0/5 - std::vector AppType_table; // FIG0/13 - std::vector programType_table; // FIG017 - std::vector announcement_table; // FIG0/18 + std::vector SId_table; + std::vector subChannel_table; // FIG0/1 + std::vector SC_C_table; // FIG0/2 + std::vector SC_P_table; // FIG0/3 + std::vector SC_G_table; // FIG0/8 + std::vector language_table; // FIG0/5 + std::vector AppType_table; // FIG0/13 + std::vector programType_table; // FIG017 + std::vector announcement_table; // FIG0/18 int32_t dateTime [8]; void reset (); diff --git a/sources/frontend/fib-decoder.cpp b/sources/frontend/fib-decoder.cpp index 9561b350..4df8ad78 100644 --- a/sources/frontend/fib-decoder.cpp +++ b/sources/frontend/fib-decoder.cpp @@ -1283,6 +1283,78 @@ uint8_t fibDecoder::serviceType (const int index) { return currentConfig -> SC_C_table [index]. TMid; } +int fibDecoder::getNrComps (const uint32_t SId) { + for (auto &SId_element : currentConfig -> SId_table) + if (SId_element. SId == SId) + return SId_element. comps. size (); + return 0; +} + +// required for ETI generation +int fibDecoder::nrChannels () { + return currentConfig -> subChannel_table. size (); +} + +// +// for primary services we return the index of the first +// component, the secondary services, the index of the +// component with the matching SCIds +// +int fibDecoder::getServiceComp (const QString &service) { +// first we check to see if the service is a primary one + for (auto &serv : theEnsemble. primaries) { + if (serv. name != service) + continue; + for (auto & SId_element: currentConfig -> SId_table) { + if (SId_element. SId == serv. SId) + return SId_element. comps [0]; + } + } + + for (auto &serv : theEnsemble. secondaries) { + if (serv. name != service) + continue; + return getServiceComp_SCIds (serv. SId, serv. SCIds); + } + return -1; +} +// +// Find the component with the indicated number +int fibDecoder::getServiceComp (const uint32_t SId, + const int compnr) { + for (auto &SId_element : currentConfig -> SId_table) { + if (SId_element. SId == SId) { + return SId_element. comps [compnr]; + } + } + return -1; +} + +// Find the component with the indicated SCIds +int fibDecoder::getServiceComp_SCIds (const uint32_t SId, + const int SCIds) { +// fprintf (stderr, "Looking for serviceComp %X %d\n", SId, SCIds); + for (auto &SId_element : currentConfig -> SId_table) { + if (SId_element. SId != SId) + continue; + for (int i = 0; i < (int) SId_element. comps. size (); i ++) { + int index = SId_element. comps [i]; + if (currentConfig -> SCIdsOf (index) == SCIds) + return index; + } + } + return -1; +} +// +// +bool fibDecoder::isPrimary (const QString &s) { + for (auto &serv : theEnsemble. primaries) { + if (s == serv. name) + return true; + } + return false; +} + void fibDecoder::audioData (const int index, audiodata &ad) { fibConfig::serviceComp_C &comp = currentConfig -> SC_C_table [index]; for (auto &serv : theEnsemble. primaries) { @@ -1344,69 +1416,13 @@ fibConfig::serviceComp_C &comp = currentConfig -> SC_C_table [index]; pd. defined = true; } -int fibDecoder::getNrComps (const uint32_t SId) { - for (auto &SId_element : currentConfig -> SId_table) - if (SId_element. SId == SId) - return SId_element. comps. size (); +uint16_t fibDecoder::getAnnouncing (uint16_t SId) { + for (auto &serv : currentConfig -> SId_table) + if (serv. SId == SId) + return serv. announcing; return 0; } -// -// for primary services we return the index of the first -// component, the secondary services, the index of the -// component with the matching SCIds -// -int fibDecoder::getServiceComp (const QString &service) { -// first we check to see if the service is a primary one - for (auto &serv : theEnsemble. primaries) { - if (serv. name != service) - continue; - for (auto & SId_element: currentConfig -> SId_table) { - if (SId_element. SId == serv. SId) - return SId_element. comps [0]; - } - } - - for (auto &serv : theEnsemble. secondaries) { - if (serv. name != service) - continue; - return getServiceComp_SCIds (serv. SId, serv. SCIds); - } - return -1; -} -int fibDecoder::getServiceComp (const uint32_t SId, - const int compnr) { - for (auto &SId_element : currentConfig -> SId_table) { - if (SId_element. SId == SId) { - return SId_element. comps [compnr]; - } - } - return -1; -} - -int fibDecoder::getServiceComp_SCIds (const uint32_t SId, - const int SCIds) { -// fprintf (stderr, "Looking for serviceComp %X %d\n", SId, SCIds); - for (auto &SId_element : currentConfig -> SId_table) { - if (SId_element. SId != SId) - continue; - for (int i = 0; i < (int) SId_element. comps. size (); i ++) { - int index = SId_element. comps [i]; - if (currentConfig -> SCIdsOf (index) == SCIds) - return index; - } - } - return -1; -} - -bool fibDecoder::isPrimary (const QString &s) { - for (auto &serv : theEnsemble. primaries) { - if (s == serv. name) - return true; - } - return false; -} - std::vector fibDecoder::getFrequency (const QString &s) { std::vector res; for (auto &serv : theEnsemble. primaries) { @@ -1416,20 +1432,17 @@ std::vector res; return res; } -// required for ETI generation -int fibDecoder::nrChannels () { - return currentConfig -> subChannel_table. size (); -} // // needed for generating eti files void fibDecoder::getChannelInfo (channel_data *d, const int n) { +fibConfig::subChannel *selected = &(currentConfig -> subChannel_table [n]); d -> in_use = true; - d -> id = currentConfig -> subChannel_table [n]. subChId; - d -> start_cu = currentConfig -> subChannel_table [n]. startAddr; - d -> protlev = currentConfig -> subChannel_table [n]. protLevel; - d -> size = currentConfig -> subChannel_table [n]. Length; - d -> bitrate = currentConfig -> subChannel_table [n]. bitRate; - d -> uepFlag = currentConfig -> subChannel_table [n]. shortForm; + d -> id = selected -> subChId; + d -> start_cu = selected -> startAddr; + d -> protlev = selected -> protLevel; + d -> size = selected -> Length; + d -> bitrate = selected -> bitRate; + d -> uepFlag = selected -> shortForm; } int32_t fibDecoder::getCIFcount () { @@ -1456,13 +1469,6 @@ void fibDecoder::handleAnnouncement (uint16_t SId, uint16_t flags, } } -uint16_t fibDecoder::getAnnouncing (uint16_t SId) { - for (auto &serv : currentConfig -> SId_table) - if (serv. SId == SId) - return serv. announcing; - return 0; -} - int fibDecoder::freeSpace () { return currentConfig -> freeSpace (); } diff --git a/sources/frontend/fib-decoder.h b/sources/frontend/fib-decoder.h index 6e85120f..b3aced74 100644 --- a/sources/frontend/fib-decoder.h +++ b/sources/frontend/fib-decoder.h @@ -44,25 +44,28 @@ public: void connectChannel (); void disconnectChannel (); bool syncReached (); - - uint16_t getAnnouncing (uint16_t); +// +// The real interface uint32_t getSId (int); uint8_t serviceType (int); + int getNrComps (const uint32_t); +// +// not well chosen name, "subChannels" are meant + int nrChannels (); int getServiceComp (const QString &); int getServiceComp (const uint32_t, const int); int getServiceComp_SCIds (const uint32_t, const int); bool isPrimary (const QString &); void audioData (const int, audiodata &); void packetData (const int, packetdata &); - int getNrComps (const uint32_t); - int nrChannels (); + uint16_t getAnnouncing (uint16_t); std::vector getFrequency (const QString &); void getChannelInfo (channel_data *, const int); int32_t getCIFcount (); void getCIFcount (int16_t &, int16_t &); uint32_t julianDate (); - QList contentPrint (); int freeSpace (); + QList contentPrint (); protected: void processFIB (uint8_t *, uint16_t); diff --git a/sources/frontend/ofdm-decoder.cpp b/sources/frontend/ofdm-decoder.cpp index b224fce9..c69d06d6 100644 --- a/sources/frontend/ofdm-decoder.cpp +++ b/sources/frontend/ofdm-decoder.cpp @@ -43,8 +43,10 @@ #define ALPHA 0.005f static inline Complex normalize (const Complex &V) { -DABFLOAT Length = jan_abs (V); - return Length == 0.0f ? Complex (0.0, 0.0) : V / (DABFLOAT)Length; +DABFLOAT length = jan_abs (V); + if (length < 0.001) + return Complex (0, 0); + return Complex (V) / length; } ofdmDecoder::ofdmDecoder (RadioInterface *mr, @@ -103,7 +105,7 @@ void ofdmDecoder::reset () { angleVector [i] = M_PI_4; } meanValue = 1.0f; - avgBit = 1.0f; + avgBit = 10.0f; } // void ofdmDecoder::processBlock_0 (std::vector buffer) { @@ -113,8 +115,6 @@ void ofdmDecoder::processBlock_0 (std::vector buffer) { // memcpy (phaseReference. data (), buffer. data (), T_u * sizeof (Complex)); - - Complex temp = Complex (0, 0); } // // Just interested. In the ideal case the constellation of the @@ -184,7 +184,7 @@ void limit_symmetrically (DABFLOAT &v, DABFLOAT limit) { // // Decoder 4 is an interpretation of the so-called "Optimal 3" // version in the aforementioned paper. -// It is still experimental!!! +// It does not work well static inline Complex w (DABFLOAT kn) { @@ -203,14 +203,34 @@ DABFLOAT IO (DABFLOAT x) { return std::cyl_bessel_i (0.0f, x); } +Complex optimum3 (Complex S, Complex prevS, DABFLOAT sigmaSQ) { + Complex rr = Complex (0, 1) * conj (S); + S = S * conj (rr); + prevS = prevS * conj (rr); + DABFLOAT P1 = makeA (1, S, prevS) / sigmaSQ; + DABFLOAT P7 = makeA (7, S, prevS) / sigmaSQ; + DABFLOAT P3 = makeA (3, S, prevS) / sigmaSQ; + DABFLOAT P5 = makeA (5, S, prevS) / sigmaSQ; + + DABFLOAT IO_P1 = IO (P1); + DABFLOAT IO_P7 = IO (P7); + DABFLOAT IO_P3 = IO (P3); + DABFLOAT IO_P5 = IO (P5); + + DABFLOAT b1 = log ((IO_P1 + IO_P7) / (IO_P3 + IO_P5)); + DABFLOAT b2 = log ((IO_P1 + IO_P3) / (IO_P5 + IO_P7)); + return Complex (b1, b2); +} + void ofdmDecoder::decode (std::vector &buffer, int32_t blkno, std::vector &softbits, DABFLOAT snr) { DABFLOAT sum = 0; -DABFLOAT bitSum = 0; - +static DABFLOAT bitSum = 10; +float oldSum = bitSum; + bitSum = 0; memcpy (fft_buffer. data (), &((buffer. data ()) [T_g]), T_u * sizeof (Complex)); // first step: do the FFT @@ -224,14 +244,14 @@ DABFLOAT bitSum = 0; int16_t index = myMapper. mapIn (i); if (index < 0) index += T_u; - Complex fftBin = fft_buffer [index] * - normalize (conj (phaseReference [index])); + Complex current = fft_buffer [index]; + Complex prevS = phaseReference [index]; + Complex fftBin = current * normalize (conj (prevS)); conjVector [index] = fftBin; DABFLOAT binAbsLevel = jan_abs (fftBin); - Complex prevS = phaseReference [index]; // // updates - + Complex fftBin_at_1 = Complex (abs (real (fftBin)), abs (imag (fftBin))); @@ -292,45 +312,31 @@ DABFLOAT bitSum = 0; sum += jan_abs (R1); } - else - if (this -> decoder == DECODER_3) { // decoder 3 + else { + softbits [i] = - real (fftBin) / binAbsLevel * 1.0 * MAX_VITERBI; softbits [carriers + i] = - imag (fftBin) / binAbsLevel * 1.0 * MAX_VITERBI; } - else { // experimental decoder 4 - DABFLOAT P1 = makeA (1, fftBin, prevS) / - sigmaSQ_Vector [index]; - DABFLOAT P7 = makeA (7, fftBin, prevS) / - sigmaSQ_Vector [index]; - DABFLOAT P3 = makeA (3, fftBin, prevS) / - sigmaSQ_Vector [index]; - DABFLOAT P5 = makeA (5, fftBin, prevS) / - sigmaSQ_Vector [index]; - - DABFLOAT IO_P1 = IO (P1); - DABFLOAT IO_P7 = IO (P7); - DABFLOAT IO_P3 = IO (P3); - DABFLOAT IO_P5 = IO (P5); - - DABFLOAT b1 = log ((IO_P1 + IO_P7) / (IO_P3 + IO_P5)); - DABFLOAT b2 = log ((IO_P1 + IO_P3) / (IO_P5 + IO_P7)); - - bitSum += (abs (b1) + abs (b2)) / 2; - DABFLOAT scaler = 40 / avgBit; - int s1 = -real (fftBin) < 0 ? -1 : 1; - int s2 = -imag (fftBin) < 0 ? -1 : 1; - DABFLOAT xx1 = s1 * abs (b1) * scaler; - DABFLOAT xx2 = s2 * abs (b2) * scaler; - limit_symmetrically (xx1, MAX_VITERBI); - limit_symmetrically (xx2, MAX_VITERBI); - softbits [i] = (int16_t)xx1; - softbits [carriers + i] = (int16_t)xx2; - } +// else // experimental decoder 3 +// Interesting experiment, but does not work properly +// if (this -> decoder == DECODER_3) { // decoder 3 +// Complex res = optimum3 (current, prevS, +// sigmaSQ_Vector [index]); +// bitSum += abs (res); +// DABFLOAT scaler = 64.0 / (oldSum / carriers) ; +// DABFLOAT xx1 = - real (res) / scaler; +// DABFLOAT xx2 = - imag (res) / scaler; +// limit_symmetrically (xx1, MAX_VITERBI); +// limit_symmetrically (xx2, MAX_VITERBI); +// softbits [i] = (int16_t)xx1; +// softbits [carriers + i] = (int16_t)xx2; +// } } - avgBit = compute_avg (avgBit, bitSum / carriers, 0.1); + + avgBit = compute_avg (avgBit, bitSum / carriers, 0.1); meanValue = compute_avg (meanValue, sum /carriers, 0.1); // end of decoding , now for displaying things // diff --git a/sources/frontend/sample-reader.cpp b/sources/frontend/sample-reader.cpp index 9cbaebd2..701e09d4 100644 --- a/sources/frontend/sample-reader.cpp +++ b/sources/frontend/sample-reader.cpp @@ -153,7 +153,7 @@ auto *buffer = dynVec (std::complex, nrSamples); dcReal = compute_avg (dcReal, real (v), Alpha); dcImag = compute_avg (dcImag, imag (v), Alpha); v = std::complex (real (v) - dcReal, imag (v) - dcImag); - v = theEqualizer. equalize (v); +// v = theEqualizer. equalize (v); DABFLOAT real_V = abs (real (v)); DABFLOAT imag_V = abs (imag (v)); IQ_Real = compute_avg (IQ_Real, real_V, Alpha); @@ -164,6 +164,7 @@ auto *buffer = dynVec (std::complex, nrSamples); ((IQ_Real + IQ_Imag) / 2)); teller = 0; } +// v = std::complex (IQ_Real, IQ_Imag); } // first: adjust frequency. We need Hz accuracy diff --git a/sources/main/radio.cpp b/sources/main/radio.cpp index 68aa2bb8..74787caf 100644 --- a/sources/main/radio.cpp +++ b/sources/main/radio.cpp @@ -1922,8 +1922,10 @@ bool RadioInterface::eventFilter (QObject *obj, QEvent *event) { return true; } else // handling function keys - if (handle_keyEvent (keyEvent -> key ())) - return true; + if (QApplication::keyboardModifiers (). + testFlag (Qt::ControlModifier)) { + return handle_keyEvent (keyEvent -> key ()); + } } // An option is to click - right hand mouse button - on a // service in the scanlist in order to add it to the @@ -4779,55 +4781,35 @@ void RadioInterface::focusInEvent (QFocusEvent *evt) { // // This function is called whenever a key is touched // that is not the return key +// as it turns out, our "beloved" windows does not let +// the Qt user catch the functon keys, we settle for Ctrl Ii bool RadioInterface::handle_keyEvent (int theKey) { - switch (theKey) { - case Qt::Key_F1: - theEnsembleHandler -> activateWindow (); - theEnsembleHandler -> setFocus (); - return true; + if (theKey != Qt::Key_I) + return false; - case Qt::Key_F2: - configHandler_p -> activateWindow (); - configHandler_p -> setFocus (); - return true; - - case Qt::Key_F3: - techWindow_p -> activateWindow (); - techWindow_p -> setFocus (); - return true; - - case Qt::Key_F4: - this -> activateWindow (); - this -> setFocus (); - return true; - - case Qt::Key_F6: - if (theEnsembleHandler -> hasFocus ()) { - this -> activateWindow (); - this -> setFocus (); - return true; - } - else - if (this -> hasFocus ()) { - configHandler_p -> activateWindow (); - configHandler_p -> setFocus (); - return true; - } - else - if (configHandler_p -> hasFocus ()) { - techWindow_p -> activateWindow (); - techWindow_p -> setFocus (); - return true; - } - else - if (techWindow_p -> hasFocus ()) { - theEnsembleHandler -> activateWindow (); - theEnsembleHandler -> setFocus (); - return true; - } - else - return false; - default: - return false; + if (theEnsembleHandler -> hasFocus ()) { + this -> activateWindow (); + this -> setFocus (); + return true; } + else + if (this -> hasFocus ()) { + configHandler_p -> activateWindow (); + configHandler_p -> setFocus (); + return true; + } + else + if (configHandler_p -> hasFocus ()) { + techWindow_p -> activateWindow (); + techWindow_p -> setFocus (); + return true; + } + else + if (techWindow_p -> hasFocus ()) { + theEnsembleHandler -> activateWindow (); + theEnsembleHandler -> setFocus (); + return true; + } + return false; } +