mirror of
https://github.com/saitohirga/WSJT-X.git
synced 2024-11-19 02:22:10 -05:00
Forgot to include four files.
git-svn-id: svn+ssh://svn.code.sf.net/p/wsjt/wsjt/branches/wsjtx@3526 ab8295b8-cf94-4d9e-aec4-7959e3be5d79
This commit is contained in:
parent
37f9eada62
commit
324169661e
93
Detector.cpp
Normal file
93
Detector.cpp
Normal file
@ -0,0 +1,93 @@
|
||||
#include "Detector.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QDebug>
|
||||
|
||||
#include "commons.h"
|
||||
|
||||
Detector::Detector (unsigned frameRate, unsigned periodLengthInSeconds, unsigned bytesPerSignal, QObject * parent)
|
||||
: QIODevice (parent)
|
||||
, m_frameRate (frameRate)
|
||||
, m_period (periodLengthInSeconds)
|
||||
, m_bytesPerSignal (bytesPerSignal)
|
||||
, m_monitoring (false)
|
||||
, m_starting (false)
|
||||
{
|
||||
clear ();
|
||||
}
|
||||
|
||||
bool Detector::reset ()
|
||||
{
|
||||
clear ();
|
||||
return QIODevice::reset ();
|
||||
}
|
||||
|
||||
void Detector::clear ()
|
||||
{
|
||||
// set index to roughly where we are in time (1s resolution)
|
||||
jt9com_.kin = secondInPeriod () * m_frameRate;
|
||||
|
||||
// fill buffer with zeros
|
||||
std::fill (jt9com_.d2, jt9com_.d2 + sizeof (jt9com_.d2) / sizeof (jt9com_.d2[0]), 0);
|
||||
}
|
||||
|
||||
qint64 Detector::writeData (char const * data, qint64 maxSize)
|
||||
{
|
||||
Q_ASSERT (!(maxSize % sizeof (jt9com_.d2[0]))); // no torn frames
|
||||
Q_ASSERT (!(reinterpret_cast<size_t> (data) % __alignof__ (frame_t))); // data is aligned as frame_t would be
|
||||
|
||||
frame_t const * frames (reinterpret_cast<frame_t const *> (data));
|
||||
|
||||
qint64 framesAcceptable (sizeof (jt9com_.d2) / sizeof (jt9com_.d2[0]) - jt9com_.kin);
|
||||
qint64 framesAccepted (std::min (maxSize / sizeof (jt9com_.d2[0]), framesAcceptable));
|
||||
|
||||
if (framesAccepted < maxSize / sizeof (jt9com_.d2[0]))
|
||||
{
|
||||
qDebug () << "dropped " << maxSize / sizeof (jt9com_.d2[0]) - framesAccepted << " frames of data on the floor!\n";
|
||||
}
|
||||
|
||||
std::copy (frames, frames + framesAccepted, &jt9com_.d2[jt9com_.kin]);
|
||||
|
||||
unsigned lastSignalIndex (jt9com_.kin * sizeof (jt9com_.d2[0]) / m_bytesPerSignal);
|
||||
jt9com_.kin += framesAccepted;
|
||||
unsigned currentSignalIndex (jt9com_.kin * sizeof (jt9com_.d2[0]) / m_bytesPerSignal);
|
||||
|
||||
if (currentSignalIndex != lastSignalIndex && m_monitoring)
|
||||
{
|
||||
Q_EMIT bytesWritten (currentSignalIndex * m_bytesPerSignal);
|
||||
}
|
||||
|
||||
if (!secondInPeriod ())
|
||||
{
|
||||
if (!m_starting)
|
||||
{
|
||||
// next samples will be in new period so wrap around to
|
||||
// start of buffer
|
||||
//
|
||||
// we don't bother calling reset () since we expect to fill
|
||||
// the whole buffer and don't need to waste cycles zeroing
|
||||
jt9com_.kin = 0;
|
||||
m_starting = true;
|
||||
}
|
||||
}
|
||||
else if (m_starting)
|
||||
{
|
||||
m_starting = false;
|
||||
}
|
||||
|
||||
return maxSize; // we drop any data past the end of
|
||||
// the buffer on the floor until the
|
||||
// next period starts
|
||||
}
|
||||
|
||||
unsigned Detector::secondInPeriod () const
|
||||
{
|
||||
// we take the time of the data as the following assuming no latency
|
||||
// delivering it to us (not true but close enough for us)
|
||||
qint64 now (QDateTime::currentMSecsSinceEpoch ());
|
||||
|
||||
unsigned secondInToday ((now % 86400000LL) / 1000);
|
||||
return secondInToday % m_period;
|
||||
}
|
67
Detector.hpp
Normal file
67
Detector.hpp
Normal file
@ -0,0 +1,67 @@
|
||||
#ifndef DETECTOR_HPP__
|
||||
#define DETECTOR_HPP__
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <QIODevice>
|
||||
|
||||
//
|
||||
// output device that distributes data in predefined chunks via a signal
|
||||
//
|
||||
// the underlying device for this abstraction is just the buffer that
|
||||
// stores samples throughout a receiving period
|
||||
//
|
||||
class Detector : public QIODevice
|
||||
{
|
||||
Q_OBJECT;
|
||||
|
||||
Q_PROPERTY (bool monitoring READ isMonitoring WRITE setMonitoring);
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY (Detector);
|
||||
|
||||
public:
|
||||
//
|
||||
// if the data buffer were not global storage and fixed size then we
|
||||
// might want maximum size passed as constructor arguments
|
||||
//
|
||||
Detector (unsigned frameRate, unsigned periodLengthInSeconds, unsigned bytesPerSignal, QObject * parent = 0);
|
||||
|
||||
bool open ()
|
||||
{
|
||||
// we only support data consumption and want it as fast as possible
|
||||
return QIODevice::open (QIODevice::WriteOnly | QIODevice::Unbuffered);
|
||||
}
|
||||
|
||||
bool isSequential () const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isMonitoring () const {return m_monitoring;}
|
||||
void setMonitoring (bool newState) {m_monitoring = newState;}
|
||||
|
||||
bool reset ();
|
||||
|
||||
protected:
|
||||
qint64 readData (char * /* data */, qint64 /* maxSize */)
|
||||
{
|
||||
return -1; // we don't produce data
|
||||
}
|
||||
|
||||
qint64 writeData (char const * data, qint64 maxSize);
|
||||
|
||||
private:
|
||||
typedef int16_t frame_t;
|
||||
|
||||
void clear (); // discard buffer contents
|
||||
unsigned secondInPeriod () const;
|
||||
|
||||
unsigned m_frameRate;
|
||||
unsigned m_period;
|
||||
unsigned m_bytesPerSignal;
|
||||
bool m_monitoring;
|
||||
bool m_starting;
|
||||
};
|
||||
|
||||
#endif
|
195
Modulator.cpp
Normal file
195
Modulator.cpp
Normal file
@ -0,0 +1,195 @@
|
||||
#include "Modulator.hpp"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
|
||||
#include <QDateTime>
|
||||
|
||||
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<unsigned>::max ()) // ensure we set up first symbol tone
|
||||
{
|
||||
}
|
||||
|
||||
bool Modulator::open (std::vector<int> const * symbols, std::vector<int> 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<frame_t>::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<frame_t *> (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<frame_t>::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<unsigned> ((*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<frame_t>::max ())
|
||||
{
|
||||
i4 = std::numeric_limits<frame_t>::max ();
|
||||
}
|
||||
if (i4 < std::numeric_limits<frame_t>::min ())
|
||||
{
|
||||
i4 = std::numeric_limits<frame_t>::min ();
|
||||
}
|
||||
frame = i4;
|
||||
}
|
||||
return frame;
|
||||
}
|
81
Modulator.hpp
Normal file
81
Modulator.hpp
Normal file
@ -0,0 +1,81 @@
|
||||
#ifndef MODULATOR_HPP__
|
||||
#define MODULATOR_HPP__
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <QIODevice>
|
||||
#include <QScopedPointer>
|
||||
|
||||
//
|
||||
// Input device that generates PCM audio frames that encode a message
|
||||
// and an optional CW ID.
|
||||
//
|
||||
// Output can be muted while underway, preserving waveform timing when
|
||||
// transmission is resumed.
|
||||
//
|
||||
class Modulator : public QIODevice
|
||||
{
|
||||
Q_OBJECT;
|
||||
|
||||
Q_PROPERTY (unsigned frequency READ frequency WRITE setFrequency);
|
||||
Q_PROPERTY (bool tuning READ isTuning WRITE tune);
|
||||
Q_PROPERTY (bool muted READ isMuted WRITE mute);
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY (Modulator);
|
||||
|
||||
public:
|
||||
Modulator (unsigned frameRate, unsigned periodLengthInSeconds, QObject * parent = 0);
|
||||
|
||||
bool isTuning () const {return m_tuning;}
|
||||
Q_SLOT void tune (bool newState = true) {m_tuning = newState;}
|
||||
|
||||
bool isMuted () const {return m_muted;}
|
||||
Q_SLOT void mute (bool newState = true) {m_muted = newState;}
|
||||
|
||||
unsigned frequency () const {return m_frequency;}
|
||||
Q_SLOT void setFrequency (unsigned newFrequency) {m_frequency = newFrequency;}
|
||||
|
||||
bool open (std::vector<int> const * symbols, std::vector<int> const * cw, double framesPerSymbol, unsigned frequency, double dBSNR = 99.);
|
||||
|
||||
bool isSequential () const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
qint64 readData (char * data, qint64 maxSize);
|
||||
qint64 writeData (char const * /* data */, qint64 /* maxSize */)
|
||||
{
|
||||
return -1; // we don't consume data
|
||||
}
|
||||
|
||||
private:
|
||||
typedef short frame_t;
|
||||
|
||||
frame_t postProcessFrame (frame_t frame) const;
|
||||
|
||||
QScopedPointer<std::vector<int> const> m_symbols;
|
||||
QScopedPointer<std::vector<int> const> m_cw;
|
||||
|
||||
static double const m_twoPi;
|
||||
static unsigned const m_nspd; // CW ID WPM factor
|
||||
|
||||
int m_frameRate;
|
||||
int m_period;
|
||||
double m_nsps;
|
||||
double m_frequency;
|
||||
double m_snr;
|
||||
enum {Idle, Active, Done} m_state;
|
||||
bool m_tuning;
|
||||
bool m_muted;
|
||||
bool m_addNoise;
|
||||
double m_phi;
|
||||
double m_dphi;
|
||||
double m_amp;
|
||||
unsigned m_ic;
|
||||
double m_fac;
|
||||
unsigned m_isym0;
|
||||
};
|
||||
|
||||
#endif
|
@ -1,4 +1,4 @@
|
||||
//--------------------------------------------------------------- MainWindow
|
||||
//---------------------------------------------------------------- MainWindow
|
||||
#include "mainwindow.h"
|
||||
#include "ui_mainwindow.h"
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user