1
0
mirror of https://github.com/JvanKatwijk/dabradio synced 2025-10-06 00:02:49 +02:00
Files
SDR-DAB_dabradio/devices/elad-s1-handler/elad-worker.cpp
JvanKatwijk ea6f006b3e first commit
2018-03-06 20:05:31 +01:00

291 lines
9.2 KiB
C++

#
/*
* Copyright (C) 2014
* Jan van Katwijk (J.vanKatwijk@gmail.com)
* Lazy Chair Programming
*
* This file is part of the Qt-DAB
* Qt-DAB 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 2 of the License, or
* (at your option) any later version.
*
* Qt-DAB 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 Qt-DAB; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "dab-constants.h" // some general definitions
#include "elad-worker.h" // our header
#include "elad-handler.h"
#include "elad-loader.h" // the API definition
#include "ringbuffer.h" // the buffer
// The elad-worker is a simple wrapper around the elad
// interface. It is a pretty simple thread performing the
// basic functions, it reads the bytes, converts them to
// samples and converts the rate to 2048000
//
eladWorker::eladWorker (int32_t defaultFreq,
eladLoader *f,
eladHandler *h,
RingBuffer<std::complex<float>> *theBuffer,
bool *OK) {
int i;
fprintf (stderr, "creating a worker\n");
_I_Buffer = new RingBuffer<uint8_t> (16 * 32768);
fprintf (stderr, "local buffer allocated\n");
this -> theRate = 3072000;
this -> defaultFreq = defaultFreq;
this -> functions = f;
this -> theBuffer = theBuffer;
*OK = false; // just the default
iqSwitch = false;
theFilter = new eladFilter (5, 1024000, 3072000);
conversionNumber = theRate == 192000 ? 1:
theRate <= 3072000 ? 2 : 3;
//
// we convert to complexes directly
iqSize = conversionNumber == 3 ? 4 : 8;
convBufferSize = theRate / 1000;
convIndex = 0;
convBuffer = new std::complex<float> [convBufferSize + 1];
fprintf (stderr, "iqSize = %d, conversion = %d\n",
iqSize, conversionNumber);
// The sizes of the mapTable and the convTable are
// predefined and follow from the input and output rate
// (theRate / 1000) vs (2048000 / 1000)
for (i = 0; i < 2048; i ++) {
float inVal = float (theRate / 1000);
mapTable_int [i] = int (floor (i * (inVal / 2048.0)));
mapTable_float [i] = i * (inVal / 2048.0) - mapTable_int [i];
}
fprintf (stderr, "mapTables initialized\n");
//
fprintf (stderr, "testing functions\n");
if (!functions -> OK ())
return;
lastFrequency = defaultFreq; // the parameter!!!!
runnable = true;
fprintf (stderr, "functions are OK\n");
functions -> StartFIFO (functions -> getHandle ());
connect (this, SIGNAL (show_eladFrequeny (int)),
h, SLOT (show_eladFrequency (int)));
connect (this, SIGNAL (show_iqSwitch (bool)),
h, SLOT (show_iqSwitch (bool)));
start ();
*OK = true;
}
// As usual, killing objects containing a thread need to
// be done carefully.
void eladWorker::stop (void) {
if (runnable)
functions -> StopFIFO (functions -> getHandle ());
runnable = false;
}
eladWorker::~eladWorker (void) {
stop ();
while (isRunning ())
msleep (1);
delete _I_Buffer;
delete [] convBuffer;
}
std::complex<float> makeSample_31bits (uint8_t *);
std::complex<float> makeSample_30bits (uint8_t *, bool);
std::complex<float> makeSample_15bits (uint8_t *);
typedef union {
struct __attribute__((__packed__)) {
float i;
float q;
} iqf;
struct __attribute__((__packed__)) {
int32_t i;
int32_t q;
} iq;
struct __attribute__((__packed__)) {
uint8_t i1;
uint8_t i2;
uint8_t i3;
uint8_t i4;
uint8_t q1;
uint8_t q2;
uint8_t q3;
uint8_t q4;
};
} iq_sample;
#define SCALE_FACTOR_30 1073741824.000
#define SCALE_FACTOR_29 536970912.000
#define SCALE_FACTOR_14 16384.000
std::complex<float> makeSample_31bits (uint8_t *buf) {
int ii = 0; int qq = 0;
int16_t i = 0;
uint8_t q0 = buf [i++];
uint8_t q1 = buf [i++];
uint8_t q2 = buf [i++];
uint8_t q3 = buf [i++];
uint8_t i0 = buf [i++];
uint8_t i1 = buf [i++];
uint8_t i2 = buf [i++];
uint8_t i3 = buf [i++];
ii = (i3 << 24) | (i2 << 16) | (i1 << 8) | i0;
qq = (q3 << 24) | (q2 << 16) | (q1 << 8) | q0;
return std::complex<float> ((float)qq / SCALE_FACTOR_30,
(float)ii / SCALE_FACTOR_30);
return std::complex<float> ((float)ii / SCALE_FACTOR_30,
(float)qq / SCALE_FACTOR_30);
}
std::complex<float> makeSample_30bits (uint8_t *buf, bool flag) {
int ii = 0; int qq = 0;
int16_t i = 0;
uint8_t q0 = buf [i++];
uint8_t q1 = buf [i++];
uint8_t q2 = buf [i++];
uint8_t q3 = buf [i++];
uint8_t i0 = buf [i++];
uint8_t i1 = buf [i++];
uint8_t i2 = buf [i++];
uint8_t i3 = buf [i++];
ii = (i3 << 24) | (i2 << 16) | (i1 << 8) | i0;
qq = (q3 << 24) | (q2 << 16) | (q1 << 8) | q0;
if (flag)
return std::complex<float> ((float)qq / SCALE_FACTOR_29,
(float)ii / SCALE_FACTOR_29);
else
return std::complex<float> ((float)ii / SCALE_FACTOR_29,
(float)qq / SCALE_FACTOR_29);
}
//
std::complex<float> makeSample_15bits (uint8_t *buf) {
int ii = 0; int qq = 0;
int16_t i = 0;
ii = (int)((unsigned char)(buf[i++]));
ii += (int)((unsigned char)(buf[i++])) << 8;
qq = (int)((unsigned char)(buf[i++]));
qq += (int)((unsigned char)(buf[i++])) << 8;
return std::complex<float> ((float)ii / SCALE_FACTOR_14,
(float)ii / SCALE_FACTOR_14);
return std::complex<float> ((float)qq / SCALE_FACTOR_14,
(float)ii / SCALE_FACTOR_14);
}
#define BUFFER_SIZE (8 * 8192)
uint8_t buffer [BUFFER_SIZE];
//
// To make life easy, we do all handling in this task,
// its "output", i.e. the shared buffer contains the
// samples, type complex, representing a samplerate 2048000
// Every millisecond we add 2048 samples by converting
// 3072 samples
void eladWorker:: run (void) {
int32_t amount;
int rc, i;
// when (re)starting, clean up first
_I_Buffer -> FlushRingBuffer ();
fprintf (stderr, "worker thread started\n");
while (runnable) {
rc = libusb_bulk_transfer (functions -> getHandle (),
(6 | LIBUSB_ENDPOINT_IN),
(uint8_t *)buffer,
BUFFER_SIZE * sizeof (uint8_t),
&amount,
2000);
if (rc) {
fprintf (stderr,
"Error in libusb_bulk_transfer: [%d] %s\n",
rc,
libusb_error_name (rc));
if (rc != 7)
break;
}
//
// Since we do not know whether the amount read is a multiple
// of iqSize, we use an intermediate buffer
_I_Buffer -> putDataIntoBuffer (buffer, amount);
while (_I_Buffer -> GetRingBufferReadAvailable () >= iqSize * 1024) {
uint8_t myBuffer [iqSize * 1024];
_I_Buffer -> getDataFromBuffer (myBuffer, iqSize * 1024);
//
// Having read 1024 * iqSize bytes, we can make them into complex samples
// and start converting the rate
for (i = 0; i < 1024; i ++) {
convBuffer [convIndex ++] =
// theFilter -> Pass (
// makeSample_30bits (&myBuffer [iqSize * i],
// iqSwitch));
makeSample_30bits (&myBuffer [iqSize * i],
iqSwitch);
if (convIndex > convBufferSize) {
std::complex<float> temp [2048];
int16_t j;
for (j = 0; j < 2048; j ++) {
int16_t inpBase = mapTable_int [j];
float inpRatio = mapTable_float [j];
temp [j] = cmul (convBuffer [inpBase + 1], inpRatio) +
cmul (convBuffer [inpBase], 1 - inpRatio);
}
theBuffer -> putDataIntoBuffer (temp, 2048);
// shift the sample at the end to the beginning, it is needed
// as the starting sample for the next time
convBuffer [0] = convBuffer [convBufferSize];
convIndex = 1;
}
}
}
}
fprintf (stderr, "eladWorker now stopped\n");
}
void eladWorker::setVFOFrequency (int32_t f) {
int result;
int realFreq;
if (!runnable)
return;
realFreq = f % Khz (3072);
iqSwitch = ((f / Khz (3072)) & 01) == 01;
lastFrequency = f;
result = functions -> SetHWLO (functions -> getHandle (),
&lastFrequency);
if (result == 1)
fprintf (stderr, "setting frequency to %d succeeded\n",
realFreq);
else
fprintf (stderr, "setting frequency to %d failed\n",
realFreq);
show_eladFrequency (realFreq);
show_iqSwitch (iqSwitch);
}
int32_t eladWorker::getVFOFrequency (void) {
return lastFrequency;
}