Hannes Matuschek 12 years ago
commit bd880488cb

@ -12,6 +12,12 @@ IF(SDR_WITH_PORTAUDIO)
add_executable(sdr_fm sdr_fm.cc)
target_link_libraries(sdr_fm ${LIBS} libsdr)
add_executable(sdr_rec sdr_rec.cc)
target_link_libraries(sdr_rec ${LIBS} libsdr)
add_executable(sdr_wspr sdr_wspr.cc)
target_link_libraries(sdr_wspr ${LIBS} libsdr)
add_executable(sdr_afsk1200 sdr_afsk1200.cc)
target_link_libraries(sdr_afsk1200 ${LIBS} libsdr)
ENDIF(SDR_WITH_PORTAUDIO)
@ -24,3 +30,6 @@ ENDIF(SDR_WITH_QT5 AND SDR_WITH_FFTW AND SDR_WITH_PORTAUDIO)
add_executable(sdr_psk31 sdr_psk31.cc)
target_link_libraries(sdr_psk31 ${LIBS} ${QT_LIBRARIES} libsdr libsdr-gui)
add_executable(sdr_rtty sdr_rtty.cc)
target_link_libraries(sdr_rtty ${LIBS} libsdr)

@ -76,15 +76,13 @@ public:
// The input sample rate
_sampleRate = src_cfg.sampleRate();
// Symbols per bit (limit to 32 symbols per bit)
_corrLen = std::min(int(_sampleRate/_baud), 32);
// Samples per bit
_corrLen = int(_sampleRate/_baud);
// Compute symbol rate:
_symbolRate = _baud*_corrLen;
_symbolRate = std::min(10*_baud, _baud*_corrLen);
// Samples per symbol (fractional):
_mu = 0.0; _muIncr = _sampleRate/_symbolRate;
// Delayline for interpolating sub-sampler
_dl = Buffer<float>(2*8);
for (size_t i=0; i<(2*8); i++) { _dl[i] = 0; }
@ -103,14 +101,17 @@ public:
for (size_t i=0; i<_corrLen; i++) {
_markLUT[i] = std::exp(std::complex<float>(0.0, phiMark));
_spaceLUT[i] = std::exp(std::complex<float>(0.0, phiSpace));
phiMark += (2.*M_PI*_Fmark)/_symbolRate;
phiSpace += (2.*M_PI*_Fspace)/_symbolRate;
phiMark += (2.*M_PI*_Fmark)/_sampleRate;
phiSpace += (2.*M_PI*_Fspace)/_sampleRate;
_markHist[i] = 0; _spaceHist[i] = 0;
}
_lutIdx = 0;
// Get phase increment per symbol
_phase = 0; _phaseInc = _phaseMask/_corrLen;
_phase = 0; _omega = _baud/_symbolRate;
_omegaMin = _omega - 0.01*_omega;
_omegaMax = _omega + 0.01*_omega;
_gainOmega = 0.01;
// Allocate output buffer:
_buffer = Buffer<uint8_t>(src_cfg.bufferSize()/_corrLen + 1);
@ -121,7 +122,7 @@ public:
<< " samples per symbol: " << _muIncr << std::endl
<< " symbols per bit: " << _corrLen << std::endl
<< " symbol rate: " << _symbolRate << "Hz" << std::endl
<< " Phase incr/symbol: " << float(_phaseInc)/_phasePeriod;
<< " Phase incr/symbol: " << float(_omega);
Logger::get().log(msg);
@ -134,44 +135,46 @@ public:
while (i<buffer.size()) {
// Update sub-sampler
while ((_mu>1) && (i<buffer.size())) {
// Put sample into delay line
_dl[_dl_idx] = buffer[i]; _dl[_dl_idx+8] = buffer[i];
_dl_idx = (_dl_idx +1) % 8; //++; if (_dl_idx == 8) { _dl_idx = 0; }
_mu-=1; i++;
}
if (i<buffer.size()) {
// Get sample
float sample = interpolate(_dl.sub(_dl_idx, 8), _mu); _mu += _muIncr;
_markHist[_lutIdx] = sample*_markLUT[_lutIdx];
_spaceHist[_lutIdx] = sample*_spaceLUT[_lutIdx];
_markHist[_lutIdx] = float(buffer[i])*_markLUT[_lutIdx];
_spaceHist[_lutIdx] = float(buffer[i])*_spaceLUT[_lutIdx];
// Modulo LUT length
_lutIdx++; if (_lutIdx==_corrLen) { _lutIdx=0; }
float f = _getSymbol();
float symbol = _getSymbol();
// Put symbol into delay line
_dl[_dl_idx] = symbol; _dl[_dl_idx+8] = symbol;
_dl_idx = (_dl_idx+1)%8; _mu -= 1; i++;
}
if (i<buffer.size()) {
// Get interpolated symbol
float sample = interpolate(_dl.sub(_dl_idx, 8), _mu); _mu += _muIncr;
// Get symbol
_symbols <<= 1; _symbols |= (f>0);
_symbols <<= 1; _symbols |= (sample>0);
// Advance phase
_phase += _omega;
// Sample bit
if (_phase >= 1) {
// Modulo "2 pi"
_phase = fmodf(_phase, 1.0);
// Store bit
_lastBits <<= 1; _lastBits |= (_symbols & 1);
// Put decoded bit in output buffer
// transition -> 0; no transition -> 1
_buffer[o++] = ((_lastBits ^ (_lastBits >> 1) ^ 1) & 1);
}
// If transition
if ((_symbols ^ (_symbols >> 1)) & 1) {
// Phase correction
if (_phase < (_phasePeriod/2)) {
_phase += _phaseInc/8;
} else {
_phase -= _phaseInc/8;
}
}
// Advance phase
_phase += _phaseInc;
// Sample bit
if (_phase >= _phasePeriod) {
// Modulo 2 pi
_phase &= _phaseMask;
// Store bit
_lastBits <<= 1; _lastBits |= (_symbols & 1);
// Put decoded bit in output buffer
_buffer[o++] = ((_lastBits ^ (_lastBits >> 1) ^ 1) & 1);
/*std::cerr << "Transition at phi=" << _phase << std::endl
<< " update omega from " << _omega << " to "; */
if (_phase < 0.5) { _omega -= _gainOmega*(_phase); }
else { _omega += _gainOmega*(1-_phase); }
// Limit omega
_omega = std::min(_omegaMax, std::max(_omegaMin, _omega));
//std::cerr << _omega << std::endl;
}
}
}
@ -213,8 +216,9 @@ protected:
uint32_t _symbols;
uint32_t _lastBits;
uint32_t _phase;
uint32_t _phaseInc;
float _phase;
float _omega, _omegaMin, _omegaMax;
float _gainOmega;
static const uint32_t _phasePeriod = 0x10000u;
static const uint32_t _phaseMask = 0x0ffffu;
@ -248,15 +252,23 @@ public:
}
_bitstream = 0;
_bitbuffer = 0x00;
_bitbuffer = 0;
_state = 0;
_ptr = _rxbuffer;
// Allocate output buffer
_buffer = Buffer<uint8_t>(512);
// propergate config
this->setConfig(Config(Traits<uint8_t>::scalarId, 0, 512, 1));
}
virtual void process(const Buffer<uint8_t> &buffer, bool allow_overwrite) {
for (size_t i=0; i<buffer.size(); i++) {
_bitstream <<= 1; _bitstream |= buffer[i];
// Store bit in stream
_bitstream <<= 1; _bitstream |= !!buffer[i];
// Check for sync byte
if ((_bitstream & 0xff) == 0x7e) {
if (_state && ((_ptr - _rxbuffer) > 2)) {
*_ptr = 0;
@ -264,33 +276,46 @@ public:
std::cerr << "Got invalid buffer: " << _rxbuffer << std::endl;
} else {
std::cerr << "GOT: " << _rxbuffer << std::endl;
}
memcpy(_buffer.ptr(), _rxbuffer, _ptr-_rxbuffer);
this->send(_buffer.head(_ptr-_rxbuffer));
}
}
_state = 1;
_ptr = _rxbuffer;
_bitbuffer = 0x80u;
_bitbuffer = 0x80;
continue;
}
// If 7 ones are received in a row -> error, wait or sync byte
if ((_bitstream & 0x7f) == 0x7f) { _state = 0; continue; }
// If state == wait for sync byte -> receive next bit
if (!_state) { continue; }
if ((_bitstream & 0x3f) == 0x3e) { /* stuffed bit */ continue; }
/* stuffed bit */
if ((_bitstream & 0x3f) == 0x3e) { continue; }
if (_bitstream & 1) { _bitbuffer |= 0x100; }
// prepend bit to bitbuffer
_bitbuffer |= ((_bitstream & 1) << 8);
// If 8 bits have been received (stored in b8-b1 of _bitbuffer)
if (_bitbuffer & 1) {
// Check for buffer overrun
if ((_ptr-_rxbuffer) >= 512) {
Logger::get().log(LogMessage(LOG_ERROR, "AX.25 packet too long."));
_state = 0; continue;
// Wait for next sync byte
_state = 0;
continue;
}
std::cerr << "Got byte: " << std::hex << int( (_bitbuffer>>1) & 0xff )
<< ": " << char(_bitbuffer>>1)
<< "(" << char(_bitbuffer>>2) << ")" << std::endl;
*_ptr++ = (_bitbuffer >> 1); _bitbuffer = 0x80u; continue;
// Store received byte and ...
*_ptr++ = (_bitbuffer >> 1);
// reset bit buffer
_bitbuffer = 0x80;
continue;
}
// Shift bitbuffer one to the left
_bitbuffer >>= 1;
}
}
@ -302,6 +327,8 @@ protected:
uint8_t _rxbuffer[512];
uint8_t *_ptr;
Buffer<uint8_t> _buffer;
};

@ -0,0 +1,130 @@
#include "demod.hh"
#include "rtlsource.hh"
#include "baseband.hh"
#include "autocast.hh"
#include "portaudio.hh"
#include "wavfile.hh"
#include <iostream>
#include <csignal>
using namespace sdr;
static void __sigint_handler(int signo) {
std::cerr << "Stop Queue..." << std::endl;
// On SIGINT -> stop queue properly
Queue::get().stop();
Queue::get().wait();
}
int main(int argc, char *argv[]) {
if (3 > argc) {
std::cout << "USAGE: sdr_rec FREQUENCY MODE [OUTPUT.wav]" << std::endl; return -1;
}
// get frequency
double freq = atof(argv[1]);
std::string mode = argv[2];
// Get output file (if given)
std::string outFile;
if (4 <= argc) { outFile = argv[3]; }
sdr::Logger::get().addHandler(
new sdr::StreamLogHandler(std::cerr, sdr::LOG_DEBUG));
// Register handler:
signal(SIGINT, __sigint_handler);
PortAudio::init();
// obtain base-band config
double f_center = 0, f_filter = 0, flt_width = 0;
int sub_sample = 1; double out_f_sample = 12e3;
if (mode == "WFM") {
f_center = 0; f_filter = 0; flt_width = 50e3;
out_f_sample = 48e3;
} else if (mode == "NFM") {
f_center = 0; f_filter = 0; flt_width = 12.5e3;
out_f_sample = 12e3;
} else if (mode == "AM") {
f_center = 0; f_filter = 0; flt_width = 15e3;
out_f_sample = 12e3;
} else if (mode == "USB") {
f_center = 0; f_filter = 1500; flt_width = 3e3;
out_f_sample = 12e3;
} else if (mode == "LSB") {
f_center = 0; f_filter = -1500; flt_width = 3e3;
out_f_sample = 12e3;
} else {
std::cerr << "Unknown mode '" << mode
<< "': Possible values are WFM, NFM, AM, USB, LSB." << std::endl;
return -1;
}
// Create nodes
RTLSource src(freq, 1e6);
AutoCast< std::complex<int16_t> > cast;
IQBaseBand<int16_t> baseband(f_center, f_filter, flt_width, 16, sub_sample, out_f_sample);
FMDemod<int16_t> *fm_demod = 0;
FMDeemph<int16_t> *fm_deemph = 0;
AMDemod<int16_t> *am_demod = 0;
USBDemod<int16_t> *usb_demod = 0;
PortSink audio;
WavSink<int16_t> *wav_sink = 0;
if (outFile.size()) {
wav_sink = new WavSink<int16_t>(outFile);
}
// Assemble processing chain:
src.connect(&cast, true);
cast.connect(&baseband);
if (mode == "WFM") {
fm_demod = new FMDemod<int16_t>();
fm_deemph= new FMDeemph<int16_t>();
baseband.connect(fm_demod, true);
fm_demod->connect(fm_deemph, true);
fm_deemph->connect(&audio);
if (wav_sink) { fm_deemph->connect(wav_sink); }
} else if (mode == "NFM") {
fm_demod = new FMDemod<int16_t>();
fm_deemph= new FMDeemph<int16_t>();
fm_demod->connect(fm_deemph, true);
baseband.connect(fm_demod, true);
fm_demod->connect(fm_deemph, true);
fm_deemph->connect(&audio);
if (wav_sink) { fm_deemph->connect(wav_sink); }
} else if (mode == "AM") {
am_demod = new AMDemod<int16_t>();
baseband.connect(am_demod);
am_demod->connect(&audio);
if (wav_sink) { am_demod->connect(wav_sink); }
} else if ((mode == "USB") || (mode == "LSB")){
usb_demod = new USBDemod<int16_t>();
baseband.connect(usb_demod);
usb_demod->connect(&audio);
if (wav_sink) { usb_demod->connect(wav_sink); }
}
Queue::get().addStart(&src, &RTLSource::start);
Queue::get().addStop(&src, &RTLSource::stop);
std::cerr << "Start recording at " << src.frequency()
<< "Hz in mode " << mode << ". Press CTRL-C to stop recoding." << std::endl;
Queue::get().start();
Queue::get().wait();
if (fm_demod) { delete fm_demod; }
if (fm_deemph) { delete fm_deemph; }
if (usb_demod) { delete usb_demod; }
if (wav_sink) { delete wav_sink; }
PortAudio::terminate();
std::cerr << "Recording stopped." << std::endl;
return 0;
}

@ -32,7 +32,7 @@ public:
public:
/** Constructor, the filter center frequency @c Ff equals the given center frequency @c Fc. */
IQBaseBand(double Fc, double width, size_t order, size_t sub_sample, double oFs=0.0)
: Sink<CScalar>(), Source(), FreqShiftBase<Scalar>(_Fc, 0),
: Sink<CScalar>(), Source(), FreqShiftBase<Scalar>(Fc, 0),
_Fc(Fc), _Ff(Fc), _Fs(0), _width(width), _order(std::max(size_t(1), order)),
_sub_sample(sub_sample), _oFs(oFs), _ring_offset(0), _sample_count(0),
_last(0), _kernel(_order)
@ -44,7 +44,7 @@ public:
/** Constructor. */
IQBaseBand(double Fc, double Ff, double width, size_t order, size_t sub_sample, double oFs=0.0)
: Sink<CScalar>(), Source(), FreqShiftBase<Scalar>(_Fc, 0),
: Sink<CScalar>(), Source(), FreqShiftBase<Scalar>(Fc, 0),
_Fc(Fc), _Ff(Ff), _Fs(0), _width(width), _order(std::max(size_t(1), order)),
_sub_sample(sub_sample), _oFs(oFs), _ring_offset(0), _sample_count(0),
_last(0), _kernel(_order)

@ -108,6 +108,7 @@ public:
{
// pass...
}
/** Destructor. */
virtual ~USBDemod() {
// pass...

@ -4,6 +4,7 @@
#include "config.hh"
#include "traits.hh"
#include "node.hh"
#include "operators.hh"
namespace sdr {
@ -95,7 +96,7 @@ protected:
protected:
/** The size of the LUT. */
static const size_t _lut_size = 127;
static const size_t _lut_size = 128;
};

@ -5,8 +5,25 @@ using namespace sdr;
using namespace sdr::gui;
/* ********************************************************************************************* *
* SpectrumProvider
* ********************************************************************************************* */
SpectrumProvider::SpectrumProvider(QObject *parent)
: QObject(parent)
{
// pass...
}
SpectrumProvider::~SpectrumProvider() {
// pass...
}
/* ********************************************************************************************* *
* Spectrum
* ********************************************************************************************* */
Spectrum::Spectrum(double rrate, size_t fftsize, size_t max_avg, QObject *parent) :
QObject(parent), _rrate(rrate), _fft_size(fftsize), _fft_buffer(fftsize),
SpectrumProvider(parent), _rrate(rrate), _fft_size(fftsize), _fft_buffer(fftsize),
_compute(fftsize), _spectrum(fftsize), _sample_count(0), _N_samples(0),
_trafo_count(0), _Ntrafo(max_avg),
_samples_left(0), _input_type(Config::Type_UNDEFINED), _sample_rate(0)
@ -43,7 +60,7 @@ Spectrum::config(const Config &src_cfg) {
<< " Data type: " << _input_type << std::endl
<< " sample-rate: " << _sample_rate << std::endl
<< " FFT size: " << _fft_size << std::endl
<< " # sample drops: " << _N_samples << std::endl
<< " # sample drops: " << _N_samples-1 << std::endl
<< " # averages: " << _Ntrafo << std::endl
<< " refresh rate: " << _sample_rate/(_N_samples*_Ntrafo) << "Hz";
Logger::get().log(msg);
@ -69,6 +86,20 @@ Spectrum::isInputReal() const {
return false;
}
double
Spectrum::sampleRate() const {
return _sample_rate;
}
size_t
Spectrum::fftSize() const {
return _fft_size;
}
const Buffer<double> &
Spectrum::spectrum() const {
return _spectrum;
}
void
Spectrum::handleBuffer(const RawBuffer &buffer, bool allow_overwrite)

@ -9,9 +9,39 @@
namespace sdr {
namespace gui {
class SpectrumProvider: public QObject
{
Q_OBJECT
public:
SpectrumProvider(QObject *parent=0);
virtual ~SpectrumProvider();
/** Returns true if the input is real. */
virtual bool isInputReal() const = 0;
/** Retunrs the sample rate. */
virtual double sampleRate() const = 0;
/** Returns the FFT size. */
virtual size_t fftSize() const = 0;
/** Returns the current spectrum. */
virtual const Buffer<double> &spectrum() const = 0;
signals:
/** Gets emitted once the spectrum was updated. */
void spectrumUpdated();
/** Gets emitted once the spectrum was reconfigured. */
void spectrumConfigured();
};
/** Calculates the power spectrum of a singnal. The spectrum gets updated with a specified
* rate (if possible). */
class Spectrum : public QObject, public SinkBase
class Spectrum : public SpectrumProvider, public SinkBase
{
Q_OBJECT
@ -36,20 +66,13 @@ public:
bool isInputReal() const;
/** Retunrs the sample rate. */
inline double sampleRate() const { return _sample_rate; }
double sampleRate() const;
/** Returns the FFT size. */
inline size_t fftSize() const { return _fft_size; }
size_t fftSize() const;
/** Returns the current spectrum. */
inline const Buffer<double> &spectrum() const { return _spectrum; }
signals:
/** Gets emitted once the spectrum was updated. */
void spectrumUpdated();
/** Gets emitted once the spectrum was reconfigured. */
void spectrumConfigured();
const Buffer<double> &spectrum() const;
protected:
/** Updates the FFT in the _compute buffer. */

@ -8,7 +8,7 @@
using namespace sdr;
using namespace sdr::gui;
SpectrumView::SpectrumView(Spectrum *spectrum, QWidget *parent)
SpectrumView::SpectrumView(SpectrumProvider *spectrum, QWidget *parent)
: QWidget(parent), _spectrum(spectrum), _numXTicks(11), _numYTicks(6),
_maxF(std::numeric_limits<double>::infinity()), _mindB(-60)
{

@ -18,7 +18,7 @@ Q_OBJECT
public:
/** @param rrate Specifies the (approx) refreshrate of the FFT plot. */
explicit SpectrumView(Spectrum *spectrum, QWidget *parent=0);
explicit SpectrumView(SpectrumProvider *spectrum, QWidget *parent=0);
inline size_t numXTicks() const { return _numXTicks; }
inline void setNumXTicks(size_t N) { _numXTicks=N; update(); }
@ -51,7 +51,7 @@ protected:
protected:
/** Holds a weak reference to the spectrum recorder object. */
Spectrum *_spectrum;
SpectrumProvider *_spectrum;
/// The font being used for axis labels
QFont _axisFont;
/// The plot area

@ -73,7 +73,7 @@ LinearColorMap::map(const double &value) {
/* ****************************************************************************************** *
* Implementation of WaterFallView
* ****************************************************************************************** */
WaterFallView::WaterFallView(Spectrum *spectrum, size_t height, QWidget *parent)
WaterFallView::WaterFallView(SpectrumProvider *spectrum, size_t height, QWidget *parent)
: QWidget(parent), _spectrum(spectrum), _N(_spectrum->fftSize()), _M(height), _waterfall(_N,_M)
{
setMinimumHeight(height);

@ -75,7 +75,7 @@ public:
* @param spectrum Specifies the spectrum sink.
* @param height Specifies the number of PSDs to display.
* @param parent The parent widget. */
explicit WaterFallView(Spectrum *spectrum, size_t height=100, QWidget *parent = 0);
explicit WaterFallView(SpectrumProvider *spectrum, size_t height=100, QWidget *parent = 0);
signals:
void click(double f);
@ -93,7 +93,7 @@ protected slots:
protected:
/** The spectrum sink. */
Spectrum *_spectrum;
SpectrumProvider *_spectrum;
/** The size of the spectrum. */
size_t _N;
/** "Height of the spectrum. */

@ -234,22 +234,39 @@ public:
for (size_t i=0; i<16; i++) { _dl[i] = 0; }
_mu = 0;
LogMessage msg(LOG_DEBUG);
msg << "Configure InpolSubSampler node:" << std::endl
<< " by: " << _frac << std::endl
<< " type: " << src_cfg.type() << std::endl
<< " sample-rate: " << src_cfg.sampleRate()
<< " -> " << src_cfg.sampleRate()/_frac;
Logger::get().log(msg);
// Propergate config
this->setConfig(Config(Traits<oScalar>::scalarId, src_cfg.sampleRate()/_frac, bufSize, 1));
}
/** Performs the sub-sampling. */
virtual void process(const Buffer<iScalar> &buffer, bool allow_overwrite) {
virtual void process(const Buffer<iScalar> &buffer, bool allow_overwrite)
{
// Short cut
if (1 == _frac) {
this->send(buffer, allow_overwrite);
return;
}
size_t i=0, o=0;
while (i<buffer.size()) {
// Fill delay line
// First, fill sampler...
while ( (_mu > 1) && (i<buffer.size()) ) {
while ( (_mu >= 1) && (i<buffer.size()) ) {
_dl[_dl_idx] = _dl[_dl_idx+8] = buffer[i]; i++;
_dl_idx = (_dl_idx + 1) % 8; _mu -= 1;
}
while (_mu <= 1) {
// Interpolate
_buffer[o] = interpolate(_dl.sub(_dl_idx,8), _mu); _mu += _frac;
_buffer[o] = interpolate(_dl.sub(_dl_idx,8), _mu);
_mu += _frac; o++;
}
}
this->send(_buffer.head(o));
}

@ -203,9 +203,7 @@ WavSource::next()
if ((0 == _frames_left)) {
// Close file
_file.close();
Logger::get().log(LogMessage(LOG_DEBUG, "WavSource: End of file -> stop queue."));
// and signal queue to stop
Queue::get().stop();
signalEOS();
return;
}

Loading…
Cancel
Save