#include "Modulator.hpp" #include #include #include #include #include extern float gran(); // Noise generator (for tests only) double const Modulator::m_twoPi = 2.0 * 3.141592653589793238462; // float wpm=20.0; // unsigned m_nspd=1.2*48000.0/wpm; // m_nspd=3072; //18.75 WPM unsigned const Modulator::m_nspd = 2048 + 512; // 22.5 WPM Modulator::Modulator (unsigned frameRate, unsigned periodLengthInSeconds, QObject * parent) : QIODevice (parent) , m_frameRate (frameRate) , m_period (periodLengthInSeconds) , m_state (Idle) , m_phi (0.) , m_ic (0) , m_isym0 (std::numeric_limits::max ()) // ensure we set up first symbol tone { } bool Modulator::open (std::vector const * symbols, std::vector const * cw, double framesPerSymbol, unsigned frequency, double dBSNR) { m_symbols.reset (symbols); // take over ownership (cannot throw) m_cw.reset (cw); // take over ownership (cannot throw) m_addNoise = dBSNR < 0.; m_nsps = framesPerSymbol; m_frequency = frequency; m_amp = std::numeric_limits::max (); m_state = Idle; // noise generator parameters if (m_addNoise) { m_snr = std::pow (10.0, 0.05 * (dBSNR - 6.0)); m_fac = 3000.0; if (m_snr > 1.0) { m_fac = 3000.0 / m_snr; } } return QIODevice::open (QIODevice::ReadOnly); } qint64 Modulator::readData (char * data, qint64 maxSize) { frame_t * frames (reinterpret_cast (data)); unsigned numFrames (maxSize / sizeof (frame_t)); switch (m_state) { case Idle: { // Time according to this computer qint64 ms = QDateTime::currentMSecsSinceEpoch() % 86400000; unsigned mstr = ms % (1000 * m_period); if (mstr < 1000) // send silence up to first second { std::fill (frames, frames + numFrames, 0); // silence return numFrames * sizeof (frame_t); } m_ic = (mstr - 1000) * 48; std::srand (mstr); // Initialize random seed m_state = Active; } // fall through case Active: { unsigned isym (m_tuning ? 0 : m_ic / (4.0 * m_nsps)); // Actual fsample=48000 if (isym >= m_symbols->size () && (*m_cw)[0] > 0) { // Output the CW ID m_dphi = m_twoPi * m_frequency / m_frameRate; unsigned const ic0 = m_symbols->size () * 4 * m_nsps; unsigned j (0); for (unsigned i = 0; i < numFrames; ++i) { m_phi += m_dphi; if (m_phi > m_twoPi) { m_phi -= m_twoPi; } frame_t frame = std::numeric_limits::max () * std::sin (m_phi); j = (m_ic - ic0) / m_nspd + 1; if (!(*m_cw)[j]) { frame = 0; } frame = postProcessFrame (frame); *frames++ = frame; //left ++m_ic; } if (j > static_cast ((*m_cw)[0])) { m_state = Done; } return numFrames * sizeof (frame_t); } double const baud (12000.0 / m_nsps); // fade out parameters (no fade out for tuning) unsigned const i0 = m_tuning ? 999 * m_nsps : (m_symbols->size () - 0.017) * 4.0 * m_nsps; unsigned const i1 = m_tuning ? 999 * m_nsps : m_symbols->size () * 4.0 * m_nsps; for (unsigned i = 0; i < numFrames; ++i) { isym = m_tuning ? 0 : m_ic / (4.0 * m_nsps); //Actual fsample=48000 if (isym != m_isym0) { double toneFrequency = m_frequency + (*m_symbols)[isym] * baud; m_dphi = m_twoPi * toneFrequency / m_frameRate; m_isym0 = isym; } m_phi += m_dphi; if (m_phi > m_twoPi) { m_phi -= m_twoPi; } if (m_ic > i0) { m_amp = 0.98 * m_amp; } if (m_ic > i1) { m_amp = 0.0; } frame_t frame (m_amp * std::sin (m_phi)); frame = postProcessFrame (frame); *frames++ = frame; //left ++m_ic; } if (m_amp == 0.0) // TODO G4WJS: compare double with zero might not be wise { if ((*m_cw)[0] == 0) { // no CW ID to send m_state = Done; return numFrames * sizeof (frame_t); } m_phi = 0.0; } // done for this chunk - continue on next call return numFrames * sizeof (frame_t); } case Done: break; } Q_ASSERT (m_state == Done); return 0; } Modulator::frame_t Modulator::postProcessFrame (frame_t frame) const { if (m_muted) // silent frame { return 0; } if (m_addNoise) { int i4 = m_fac * (gran () + frame * m_snr / 32768.0); if (i4 > std::numeric_limits::max ()) { i4 = std::numeric_limits::max (); } if (i4 < std::numeric_limits::min ()) { i4 = std::numeric_limits::min (); } frame = i4; } return frame; }