diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 05d5617..32c61b3 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -10,8 +10,14 @@ IF(SDR_WITH_PORTAUDIO) add_executable(sdr_fm sdr_fm.cc) target_link_libraries(sdr_fm ${LIBS} libsdr) -add_executable(sdr_afsk1200 sdr_afsk1200.cc) -target_link_libraries(sdr_afsk1200 ${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) IF(SDR_WITH_QT5 AND SDR_WITH_FFTW AND SDR_WITH_PORTAUDIO) @@ -22,3 +28,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) + diff --git a/examples/sdr_afsk1200.cc b/examples/sdr_afsk1200.cc index 59caab0..95ecc31 100644 --- a/examples/sdr_afsk1200.cc +++ b/examples/sdr_afsk1200.cc @@ -76,11 +76,10 @@ public: // The input sample rate _sampleRate = src_cfg.sampleRate(); - // Symbols per bit (limit to 32 symbols per bit) - _corrLen = std::min(int(_sampleRate/_baud), 31); - + // 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; @@ -102,15 +101,17 @@ public: for (size_t i=0; i<_corrLen; i++) { _markLUT[i] = std::exp(std::complex(0.0, phiMark)); _spaceLUT[i] = std::exp(std::complex(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 - // equiv. to _phasePeriod*_baud/_symbolRate - _phase = 0; _phaseInc = _baud/_symbolRate; + _phase = 0; _omega = _baud/_symbolRate; + _omegaMin = _omega - 0.01*_omega; + _omegaMax = _omega + 0.01*_omega; + _gainOmega = 0.01; // Allocate output buffer: _buffer = Buffer(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,34 +135,24 @@ public: while (i1) && (i0); - - // If transition - if ((_symbols ^ (_symbols >> 1)) & 1) { - // Phase correction - if (_phase < 0.5) { - _phase -= _phase/10; - } else { - _phase += (1-_phase)/10; - } - } - + _symbols <<= 1; _symbols |= (sample>0); // Advance phase - _phase += _phaseInc; + _phase += _omega; // Sample bit if (_phase >= 1) { @@ -170,8 +161,21 @@ public: // 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 + /*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; + } } } this->send(_buffer.head(o)); @@ -213,7 +217,8 @@ protected: uint32_t _symbols; uint32_t _lastBits; float _phase; - float _phaseInc; + float _omega, _omegaMin, _omegaMax; + float _gainOmega; static const uint32_t _phasePeriod = 0x10000u; static const uint32_t _phaseMask = 0x0ffffu; diff --git a/examples/sdr_rec.cc b/examples/sdr_rec.cc new file mode 100644 index 0000000..bd8e6c0 --- /dev/null +++ b/examples/sdr_rec.cc @@ -0,0 +1,130 @@ +#include "demod.hh" +#include "rtlsource.hh" +#include "baseband.hh" +#include "autocast.hh" +#include "portaudio.hh" +#include "wavfile.hh" + +#include +#include + +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 > cast; + IQBaseBand baseband(f_center, f_filter, flt_width, 16, sub_sample, out_f_sample); + FMDemod *fm_demod = 0; + FMDeemph *fm_deemph = 0; + AMDemod *am_demod = 0; + USBDemod *usb_demod = 0; + PortSink audio; + WavSink *wav_sink = 0; + + if (outFile.size()) { + wav_sink = new WavSink(outFile); + } + + // Assemble processing chain: + src.connect(&cast, true); + cast.connect(&baseband); + + if (mode == "WFM") { + fm_demod = new FMDemod(); + fm_deemph= new FMDeemph(); + 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(); + fm_deemph= new FMDeemph(); + 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(); + 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(); + 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; +} diff --git a/examples/sdr_rtty.cc b/examples/sdr_rtty.cc new file mode 100644 index 0000000..301d870 --- /dev/null +++ b/examples/sdr_rtty.cc @@ -0,0 +1,48 @@ +#include "baseband.hh" +#include "autocast.hh" +#include "demod.hh" +#include "wavfile.hh" +#include "portaudio.hh" + +using namespace sdr; + +static void __sigint_handler(int signo) { + // On SIGINT -> stop queue properly + Queue::get().stop(); +} + + +int main(int argc, char *argv[]) { + if (2 != argc) { + std::cerr << "Usage: sdr_rtty FILENAME" << std::endl; + return -1; + } + + sdr::Logger::get().addHandler( + new sdr::StreamLogHandler(std::cerr, sdr::LOG_DEBUG)); + + // Register handler: + signal(SIGINT, __sigint_handler); + + PortAudio::init(); + Queue &queue = Queue::get(); + + WavSource src(argv[1]); + AutoCast< std::complex > cast; + IQBaseBand baseband(0, 2144., 200.0, 63, 1); + baseband.setCenterFrequency(2144.0); + + PortSink sink; + src.connect(&cast, true); + cast.connect(&baseband, true); + baseband.connect(&sink); + + queue.addIdle(&src, &WavSource::next); + + queue.start(); + queue.wait(); + + PortAudio::terminate(); + + return 0; +} diff --git a/examples/sdr_wspr.cc b/examples/sdr_wspr.cc new file mode 100644 index 0000000..aa834f3 --- /dev/null +++ b/examples/sdr_wspr.cc @@ -0,0 +1,72 @@ +#include "demod.hh" +#include "rtlsource.hh" +#include "baseband.hh" +#include "autocast.hh" +#include "portaudio.hh" +#include "wavfile.hh" + +#include +#include + +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 (2 > argc) { + std::cout << "USAGE: sdr_wspr FREQUENCY [OUTPUT.wav]" << std::endl; return -1; + } + + // get frequency + double freq = atof(argv[1]); + // 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(); + + + // Create nodes + RTLSource src(freq, 240e3); // <- set sample rate to 240kHz -> 20*12kHz + AutoCast< std::complex > cast; + IQBaseBand baseband(0, 1500, 3000, 16, 1, 12000); + USBDemod usb_demod; + PortSink audio; + WavSink *wav_sink = outFile.size() ? new WavSink(outFile) : 0; + + src.connect(&cast, true); + cast.connect(&baseband, true); + baseband.connect(&usb_demod, true); + usb_demod.connect(&audio, false); + 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. Press CTRL-C to stop recoding." << std::endl; + + Queue::get().start(); + Queue::get().wait(); + + if (wav_sink) { delete wav_sink; } + + PortAudio::terminate(); + + std::cerr << "Recording stopped." << std::endl; + + return 0; +} diff --git a/src/baseband.hh b/src/baseband.hh index a079575..4304fe4 100644 --- a/src/baseband.hh +++ b/src/baseband.hh @@ -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(), Source(), FreqShiftBase(_Fc, 0), + : Sink(), Source(), FreqShiftBase(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(), Source(), FreqShiftBase(_Fc, 0), + : Sink(), Source(), FreqShiftBase(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) diff --git a/src/demod.hh b/src/demod.hh index 7c7ea87..729fa43 100644 --- a/src/demod.hh +++ b/src/demod.hh @@ -108,6 +108,7 @@ public: { // pass... } + /** Destructor. */ virtual ~USBDemod() { // pass...