From cad5e766a9d26f402a658e6dad06872f07fad698 Mon Sep 17 00:00:00 2001 From: Hannes Matuschek Date: Fri, 25 Jul 2014 13:13:46 +0200 Subject: [PATCH] Fixed minors... --- examples/CMakeLists.txt | 3 + examples/sdr_psk31.cc | 191 ++++++++++++++++++++++++++++++++++++++++ src/autocast.hh | 14 ++- src/demod.hh | 2 +- src/siggen.hh | 4 +- src/wavfile.cc | 23 ++--- 6 files changed, 223 insertions(+), 14 deletions(-) create mode 100644 examples/sdr_psk31.cc diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 2421087..1a32adf 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -12,3 +12,6 @@ IF(SDR_WITH_QT5 AND SDR_WITH_FFTW AND SDR_WITH_PORTAUDIO) add_executable(sdr_rds sdr_rds.cc) target_link_libraries(sdr_rds ${LIBS} ${QT_LIBRARIES} libsdr libsdr-gui) 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) diff --git a/examples/sdr_psk31.cc b/examples/sdr_psk31.cc new file mode 100644 index 0000000..d3619ae --- /dev/null +++ b/examples/sdr_psk31.cc @@ -0,0 +1,191 @@ +#include "baseband.hh" +#include "autocast.hh" +#include "demod.hh" +#include "portaudio.hh" +#include "wavfile.hh" +#include "gui/gui.hh" + +#include + +#include +#include +#include + +using namespace sdr; + +static void __sigint_handler(int signo) { + // On SIGINT -> stop queue properly + Queue::get().stop(); +} + +template +class BPSK31: public Sink< std::complex >, public Source +{ + typedef typename Traits::SScalar SScalar; + +public: + BPSK31() + : Sink< std::complex >(), Source(), _lut(256) + { + _shift = 8*(sizeof(int16_t)-sizeof(Scalar)); + // Initialize LUT + for (size_t i=0; i<256; i++) { + _lut[i] = std::exp(std::complex(0., (-2*M_PI*i)/256))*(1<<8); + } + } + + /** Destructor. */ + virtual ~BPSK31() { + // pass... + } + + /** Configures the FM demodulator. */ + virtual void config(const Config &src_cfg) { + // Requires type & buffer size + if (!src_cfg.hasType() || !src_cfg.hasBufferSize()) { return; } + // Check if buffer type matches template + if (Config::typeId< std::complex >() != src_cfg.type()) { + ConfigError err; + err << "Can not configure BPSK31: Invalid type " << src_cfg.type() + << ", expected " << Config::typeId< std::complex >(); + throw err; + } + // Unreference buffer if non-empty + if (! _buffer.isEmpty()) { _buffer.unref(); } + // Allocate buffer + _buffer = Buffer(16); + + _lut_idx = 0; + _lut_incr = ((256.*256.*2144.)/src_cfg.sampleRate()); + _pll_incr = 0; + double tau = 1./20; + _alpha = 2*M_PI/(2*M_PI+src_cfg.sampleRate()*tau); + _lp_phase = 0; + // Results into a 4*31.25 Hz sample + _subsample = (256.*src_cfg.sampleRate()/(4*32.25)); + _sample_count = 0; + _dec_count = 0; + + LogMessage msg(LOG_DEBUG); + msg << "Configured BPSK31 node:" << std::endl + << " sample-rate: " << src_cfg.sampleRate() << std::endl + << " in-type / out-type: " << src_cfg.type() + << " / " << Config::typeId() << std::endl + << " in-place: " << (_can_overwrite ? "true" : "false") << std::endl + << " output scale: 2^" << _shift << std::endl + << " PLL tau/alpha: " << tau << "/" << _alpha; + Logger::get().log(msg); + + // Propergate config + this->setConfig(Config(Config::typeId(), 4*31.25, 16, 1)); + } + + /** Performs the FM demodulation. */ + virtual void process(const Buffer > &buffer, bool allow_overwrite) + { + if (0 == buffer.size()) { return; } + _process(buffer, _buffer); + } + +protected: + /** The actual demodulation. */ + void _process(const Buffer< std::complex > &in, const Buffer &out) + { + // Calc abs phase values + for (size_t i=1; i val = + std::complex(in[i].real(), in[i].imag()) * _lut[_lut_idx>>8]; + _lut_idx += (_lut_incr+_pll_incr); + _sample_count += 256; + // Get phase difference + double phase = std::atan2(double(val.imag()), double(val.real()))/M_PI; + // low-pass + _lp_phase = (1.-_alpha)*_lp_phase + _alpha*phase; + // Correct PLL frequency by LP-ed phase + _pll_incr += _lp_phase*10; + std::cout << "Fcorr=" << 22050. * double(_pll_incr)/(1<<16) + << "; Phi=" << 180*phase << " (" << 180*_lp_phase << ")" << std::endl; + if (_sample_count >= _subsample) { + out[_dec_count] = (_lp_phase*(1<<14)); + _sample_count -= _subsample; _dec_count++; + // If 8 bits has been collected: + if (8 == _dec_count) { + // propergate resulting 8 bits + this->send(out.head(8)); + _dec_count = 0; + } + } + } + } + +protected: + int _shift; + /** If true, in-place demodulation is poissible. */ + bool _can_overwrite; + Buffer< std::complex > _lut; + /** The current LUT index. It is defined in multiples of 256 for heiher precision. */ + uint16_t _lut_idx; + /** The LUT increment is defined in multiples of 256 for higher precision. */ + int32_t _lut_incr; + /** The PLL increment correction is defined in multiples of 256 for higher precision. */ + double _pll_incr; + double _alpha; + double _lp_phase; + size_t _subsample; + size_t _sample_count; + size_t _dec_count; + /** The output buffer, unused if demodulation is performed in-place. */ + Buffer _buffer; +}; + +int main(int argc, char *argv[]) { + if (2 != argc) { + std::cerr << "Usage: sdr_psk31 FILENAME" << std::endl; + return -1; + } + + sdr::Logger::get().addHandler( + new sdr::StreamLogHandler(std::cerr, sdr::LOG_DEBUG)); + + // Register handler: + signal(SIGINT, __sigint_handler); + + QApplication app(argc, argv); + + QMainWindow *win = new QMainWindow(); + gui::Spectrum *spec = new gui::Spectrum(2, 1024, 5); + gui::SpectrumView *spec_view = new gui::SpectrumView(spec); + spec_view->setMindB(-60); + win->setCentralWidget(spec_view); + win->setMinimumSize(640, 240); + win->show(); + + PortAudio::init(); + Queue &queue = Queue::get(); + + //WavSource src(argv[1]); + SigGen src(22050., 1024); + src.addSine(2144); + AutoCast< std::complex > cast; + IQBaseBand baseband(0.0, 2144., 400.0, 15, 1, 8000.0); + BPSK31 demod; + PortSink sink; + + src.connect(&cast, true); + cast.connect(&baseband, true); + baseband.connect(spec); + baseband.connect(&sink); + baseband.connect(&demod); + + //queue.addIdle(&src, &WavSource::next); + queue.addIdle(&src, &SigGen::next); + + queue.start(); + app.exec(); + queue.stop(); + queue.wait(); + + PortAudio::terminate(); + + return 0; +} diff --git a/src/autocast.hh b/src/autocast.hh index 1be4d06..eb44c2d 100644 --- a/src/autocast.hh +++ b/src/autocast.hh @@ -58,7 +58,7 @@ public: case Config::Type_s8: _cast = _int8_cint16; break; case Config::Type_cu8: _cast = _cuint8_cint16; break; case Config::Type_cs8: _cast = _cint8_cint16; break; - case Config::Type_u16: + case Config::Type_u16: _cast = _uint16_cint16; break; case Config::Type_s16: _cast = _int16_cint16; break; case Config::Type_cu16: case Config::Type_cs16: _cast = _identity; break; @@ -80,6 +80,7 @@ public: msg << "Configure AutoCast node:" << std::endl << " input type: " << src_cfg.type() << std::endl << " output type: " << Traits::scalarId; + Logger::get().log(msg); // Propergate config this->setConfig(Config(Config::typeId(), src_cfg.sampleRate(), src_cfg.bufferSize(), 1)); @@ -223,6 +224,17 @@ protected: return 4*N; } + /** uint16 -> complex int16. */ + static size_t _uint16_cint16(const RawBuffer &in, const RawBuffer &out) { + size_t N = in.bytesLen()/2; + int16_t *values = reinterpret_cast(in.data()); + for (size_t i=0; i *>(out.data())[i] + = std::complex(int32_t(values[i])-(2<<15)); + } + return 4*N; + } + /** int16 -> complex int16. */ static size_t _int16_cint16(const RawBuffer &in, const RawBuffer &out) { size_t N = in.bytesLen()/2; diff --git a/src/demod.hh b/src/demod.hh index daed6c9..a27723f 100644 --- a/src/demod.hh +++ b/src/demod.hh @@ -201,7 +201,7 @@ public: // Check if buffer type matches template if (Config::typeId< std::complex >() != src_cfg.type()) { ConfigError err; - err << "Can not configure USBDemod: Invalid type " << src_cfg.type() + err << "Can not configure FMDemod: Invalid type " << src_cfg.type() << ", expected " << Config::typeId< std::complex >(); throw err; } diff --git a/src/siggen.hh b/src/siggen.hh index 3e7b8ba..8b9318f 100644 --- a/src/siggen.hh +++ b/src/siggen.hh @@ -21,12 +21,12 @@ public: case Config::Type_s8: case Config::Type_cu8: case Config::Type_cs8: - _scale = 127; break; + _scale = 1<<6; break; case Config::Type_u16: case Config::Type_s16: case Config::Type_cu16: case Config::Type_cs16: - _scale = 32000; break; + _scale = 1<<14; break; default: _scale = 1; break; } diff --git a/src/wavfile.cc b/src/wavfile.cc index 6d1bcd3..509a700 100644 --- a/src/wavfile.cc +++ b/src/wavfile.cc @@ -1,5 +1,6 @@ #include "wavfile.hh" #include "config.hh" +#include "logger.hh" #include @@ -146,15 +147,15 @@ WavSource::open(const std::string &filename) _sample_rate = sample_rate; _frames_left = _frame_count; -#ifdef SDR_DEBUG - std::cerr << "Configured WavSource:" << std::endl - << " file: " << filename << std::endl - << " type:" << _type << std::endl - << " sample-rate: " << _sample_rate << std::endl - << " frame-count: " << _frame_count << std::endl - << " duration: " << _frame_count/_sample_rate << "s" << std::endl - << " buffer-size: " << _buffer_size << std::endl; -#endif + LogMessage msg(LOG_DEBUG); + msg << "Configured WavSource:" << std::endl + << " file: " << filename << std::endl + << " type:" << _type << std::endl + << " sample-rate: " << _sample_rate << std::endl + << " frame-count: " << _frame_count << std::endl + << " duration: " << _frame_count/_sample_rate << "s" << std::endl + << " buffer-size: " << _buffer_size; + Logger::get().log(msg); // unreference buffer if not empty if (! _buffer.isEmpty()) { _buffer.unref(); } @@ -195,10 +196,12 @@ WavSource::isReal() const { } void -WavSource::next() { +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(); return;