Added caller channel id in rmanager output.
Analyzer outputs quality estimation. git-svn-id: http://yate.null.ro/svn/yate/trunk@707 acf43c95-373e-0410-b603-e72c3f656dc1
This commit is contained in:
parent
fff299e2a8
commit
2e625f110d
|
@ -30,6 +30,13 @@
|
|||
|
||||
#include <yatephone.h>
|
||||
|
||||
// minimum allowed for the maximum
|
||||
#define ALLOW_MIN 2500.0
|
||||
// threshold from max we consider a peak
|
||||
#define PEAKS_THR 0.015
|
||||
// expected number of peaks
|
||||
#define PEAKS_NUM 2
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
|
@ -64,15 +71,21 @@ public:
|
|||
};
|
||||
virtual ~AsyncFFT();
|
||||
static AsyncFFT* create(unsigned int length, WinType window = Rectangle, Priority prio = Low);
|
||||
inline unsigned int length() const
|
||||
inline unsigned int samples() const
|
||||
{ return m_length; }
|
||||
inline unsigned int length() const
|
||||
{ return m_length >> 1; }
|
||||
inline bool ready() const
|
||||
{ return m_ready; }
|
||||
double operator[](int index) const;
|
||||
bool prepare(const short* samples);
|
||||
double at(int index) const;
|
||||
inline double operator[](int index) const
|
||||
{ return at(index); }
|
||||
bool prepare(const short* samp);
|
||||
inline void stop()
|
||||
{ m_stop = true; }
|
||||
{ m_notify = 0; m_stop = true; }
|
||||
virtual void run();
|
||||
inline void setNotify(Runnable* notified = 0)
|
||||
{ m_notify = notified; }
|
||||
private:
|
||||
AsyncFFT(unsigned int length, WinType window, Priority prio);
|
||||
void buildWindow(WinType window);
|
||||
|
@ -81,14 +94,16 @@ private:
|
|||
bool m_ready;
|
||||
bool m_start;
|
||||
bool m_stop;
|
||||
Runnable* m_notify;
|
||||
unsigned int m_length;
|
||||
double* m_window;
|
||||
double* m_real;
|
||||
double* m_imag;
|
||||
unsigned int m_nBits;
|
||||
const char* m_winName;
|
||||
};
|
||||
|
||||
class AnalyzerCons : public DataConsumer
|
||||
class AnalyzerCons : public DataConsumer, public Runnable
|
||||
{
|
||||
YCLASS(AnalyzerCons,DataConsumer)
|
||||
public:
|
||||
|
@ -96,6 +111,7 @@ public:
|
|||
virtual ~AnalyzerCons();
|
||||
virtual void Consume(const DataBlock& data, unsigned long tStamp);
|
||||
virtual void statusParams(String& str);
|
||||
virtual void run();
|
||||
protected:
|
||||
DataBlock m_data;
|
||||
u_int64_t m_timeStart;
|
||||
|
@ -103,6 +119,9 @@ protected:
|
|||
unsigned int m_tsGapCount;
|
||||
unsigned long m_tsGapLength;
|
||||
AsyncFFT* m_spectrum;
|
||||
unsigned long m_total;
|
||||
unsigned long m_valid;
|
||||
bool m_analyze;
|
||||
};
|
||||
|
||||
class AnalyzerChan : public Channel
|
||||
|
@ -148,9 +167,9 @@ private:
|
|||
INIT_PLUGIN(AnalyzerDriver);
|
||||
|
||||
static TokenDict dict_windows[] = {
|
||||
{ "rectangle", AsyncFFT::Rectangle },
|
||||
{ "no", AsyncFFT::None },
|
||||
{ "none", AsyncFFT::None },
|
||||
{ "rectangle", AsyncFFT::Rectangle },
|
||||
{ "triangle", AsyncFFT::Triangle },
|
||||
{ "bartlett", AsyncFFT::Bartlett },
|
||||
{ "hanning", AsyncFFT::Hanning },
|
||||
|
@ -160,6 +179,8 @@ static TokenDict dict_windows[] = {
|
|||
{ 0, 0 }
|
||||
};
|
||||
|
||||
static Mutex s_mutex;
|
||||
|
||||
static int s_res = 1;
|
||||
|
||||
static const char* printTime(char* buf,unsigned long usec)
|
||||
|
@ -198,8 +219,9 @@ AsyncFFT* AsyncFFT::create(unsigned int length, WinType window, Priority prio)
|
|||
}
|
||||
|
||||
AsyncFFT::AsyncFFT(unsigned int length, WinType window, Priority prio)
|
||||
: Thread("AsyncFFT",prio), m_ready(false), m_start(false), m_stop(false),
|
||||
m_length(0), m_window(0), m_real(0), m_imag(0), m_nBits(0)
|
||||
: Thread("AsyncFFT",prio),
|
||||
m_ready(false), m_start(false), m_stop(false), m_notify(0),
|
||||
m_length(0), m_window(0), m_real(0), m_imag(0), m_nBits(0), m_winName(0)
|
||||
{
|
||||
DDebug(&__plugin,DebugAll,"AsyncFFT::AsyncFFT(%u) [%p]",length,this);
|
||||
for (unsigned int i = 0; i <= 256 ;i++)
|
||||
|
@ -218,6 +240,7 @@ AsyncFFT::AsyncFFT(unsigned int length, WinType window, Priority prio)
|
|||
AsyncFFT::~AsyncFFT()
|
||||
{
|
||||
DDebug(&__plugin,DebugAll,"AsyncFFT::~AsyncFFT() [%p]",this);
|
||||
m_notify = 0;
|
||||
m_ready = false;
|
||||
m_start = false;
|
||||
delete[] m_real;
|
||||
|
@ -227,6 +250,7 @@ AsyncFFT::~AsyncFFT()
|
|||
|
||||
void AsyncFFT::buildWindow(WinType window)
|
||||
{
|
||||
m_winName = lookup(window,dict_windows);
|
||||
if (window == Rectangle)
|
||||
return;
|
||||
m_window = new double[m_length];
|
||||
|
@ -261,7 +285,7 @@ void AsyncFFT::buildWindow(WinType window)
|
|||
}
|
||||
}
|
||||
|
||||
double AsyncFFT::operator[](int index) const
|
||||
double AsyncFFT::at(int index) const
|
||||
{
|
||||
if ((index < 0) || ((unsigned int)index > (m_length >> 1)))
|
||||
return 0.0;
|
||||
|
@ -282,13 +306,17 @@ void AsyncFFT::run()
|
|||
m_ready = false;
|
||||
compute();
|
||||
m_ready = true;
|
||||
s_mutex.lock();
|
||||
if (m_notify)
|
||||
m_notify->run();
|
||||
s_mutex.unlock();
|
||||
m_start = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool AsyncFFT::prepare(const short* samples)
|
||||
bool AsyncFFT::prepare(const short* samp)
|
||||
{
|
||||
if (m_start || m_stop || !(samples && m_real))
|
||||
if (m_start || m_stop || !(samp && m_real))
|
||||
return false;
|
||||
m_ready = false;
|
||||
XDebug(&__plugin,DebugAll,"Preparing FFT buffer from %u samples [%p]",m_length,this);
|
||||
|
@ -296,9 +324,9 @@ bool AsyncFFT::prepare(const short* samples)
|
|||
for (i = 0; i < m_length; i++) {
|
||||
j = revBits(i);
|
||||
if (m_window)
|
||||
m_real[i] = m_window[j] * samples[j];
|
||||
m_real[i] = m_window[j] * samp[j];
|
||||
else
|
||||
m_real[i] = samples[j];
|
||||
m_real[i] = samp[j];
|
||||
m_imag[i] = 0.0;
|
||||
}
|
||||
m_start = true;
|
||||
|
@ -318,7 +346,7 @@ unsigned int AsyncFFT::revBits(unsigned int index)
|
|||
void AsyncFFT::compute()
|
||||
{
|
||||
#ifdef XDEBUG
|
||||
Debug(&__plugin,DebugAll,"Computing FFT with length %u [%p]",m_length,this);
|
||||
Debug(&__plugin,DebugInfo,"Computing FFT with length %u [%p]",m_length,this);
|
||||
Time t;
|
||||
#endif
|
||||
unsigned int i, j, n;
|
||||
|
@ -363,26 +391,29 @@ void AsyncFFT::compute()
|
|||
for (i = 0; i < n; i++)
|
||||
m_real[i] = ::sqrt(m_real[i]*m_real[i] + m_imag[i]*m_imag[i]) / n;
|
||||
#ifdef XDEBUG
|
||||
Debug(&__plugin,DebugAll,"Computing FFT with length %u took " FMT64U " usec [%p]",
|
||||
Debug(&__plugin,DebugInfo,"Computing FFT with length %u took " FMT64U " usec [%p]",
|
||||
m_length,Time::now()-t,this);
|
||||
#endif
|
||||
|
||||
for (i = 0; i < n; i ++) {
|
||||
Output("fft[%u] = %0.2f",i,m_real[i]);
|
||||
}
|
||||
#ifdef XDEBUG
|
||||
for (i = 0; i < n; i++)
|
||||
Debug(&__plugin,DebugAll,"fft[%u] = %0.2f",i,m_real[i]);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
AnalyzerCons::AnalyzerCons(const String& type, const char* window)
|
||||
: m_timeStart(0), m_tsStart(0), m_tsGapCount(0), m_tsGapLength(0),
|
||||
m_spectrum(false)
|
||||
m_spectrum(false), m_total(0), m_valid(0), m_analyze(false)
|
||||
{
|
||||
DDebug(&__plugin,DebugAll,"AnalyzerCons::AnalyzerCons('%s') [%p]",
|
||||
type.c_str(),this);
|
||||
unsigned int len = 0;
|
||||
if ((type == "spectrum") || type.startsWith("tone/probe")) {
|
||||
if ((type == "probe") || type.startsWith("tone/probe")) {
|
||||
len = 256;
|
||||
m_analyze = true;
|
||||
m_spectrum = AsyncFFT::create(len,(AsyncFFT::WinType)lookup(window,dict_windows,AsyncFFT::Rectangle));
|
||||
m_spectrum->setNotify(this);
|
||||
return;
|
||||
}
|
||||
else if (type == "fft1024")
|
||||
|
@ -395,18 +426,22 @@ AnalyzerCons::AnalyzerCons(const String& type, const char* window)
|
|||
len = 128;
|
||||
else if (type == "fft64")
|
||||
len = 64;
|
||||
if (len)
|
||||
if (len) {
|
||||
m_spectrum = AsyncFFT::create(len,(AsyncFFT::WinType)lookup(window,dict_windows,AsyncFFT::Triangle));
|
||||
m_spectrum->setNotify(this);
|
||||
}
|
||||
}
|
||||
|
||||
AnalyzerCons::~AnalyzerCons()
|
||||
{
|
||||
DDebug(&__plugin,DebugAll,"AnalyzerCons::~AnalyzerCons() %p [%p]",m_spectrum,this);
|
||||
s_mutex.lock();
|
||||
if (m_spectrum) {
|
||||
AsyncFFT* tmp = m_spectrum;
|
||||
m_spectrum = 0;
|
||||
tmp->stop();
|
||||
}
|
||||
s_mutex.unlock();
|
||||
}
|
||||
|
||||
void AnalyzerCons::Consume(const DataBlock& data, unsigned long tStamp)
|
||||
|
@ -430,7 +465,7 @@ void AnalyzerCons::Consume(const DataBlock& data, unsigned long tStamp)
|
|||
if (!m_spectrum)
|
||||
return;
|
||||
m_data += data;
|
||||
unsigned int len = 2 * m_spectrum->length();
|
||||
unsigned int len = 2 * m_spectrum->samples();
|
||||
if (m_data.length() < len)
|
||||
return;
|
||||
// limit the length of the buffer
|
||||
|
@ -443,6 +478,44 @@ void AnalyzerCons::Consume(const DataBlock& data, unsigned long tStamp)
|
|||
m_data.cut(-(int)len);
|
||||
}
|
||||
|
||||
void AnalyzerCons::run()
|
||||
{
|
||||
// this method is called with the mutex hold
|
||||
if (!m_spectrum)
|
||||
return;
|
||||
unsigned int n = m_spectrum->length();
|
||||
double max = 0.0;
|
||||
unsigned int i;
|
||||
for (i = 1; i < n; i++) {
|
||||
double val = m_spectrum->at(i);
|
||||
if (max < val)
|
||||
max = val;
|
||||
}
|
||||
|
||||
if (!m_analyze)
|
||||
return;
|
||||
|
||||
double limit = max;
|
||||
if (max < ALLOW_MIN) {
|
||||
// don't start until we get some data
|
||||
if (!m_total)
|
||||
return;
|
||||
limit = ALLOW_MIN;
|
||||
}
|
||||
|
||||
unsigned int peaks = 0;
|
||||
limit *= PEAKS_THR;
|
||||
for (i = 1; i < n; i++) {
|
||||
if (m_spectrum->at(i) > limit)
|
||||
peaks++;
|
||||
}
|
||||
DDebug(&__plugin,DebugInfo,"Got %u peaks, limit=%f, max=%f [%p]",peaks,limit,max,this);
|
||||
|
||||
m_total++;
|
||||
if (peaks == PEAKS_NUM)
|
||||
m_valid++;
|
||||
}
|
||||
|
||||
void AnalyzerCons::statusParams(String& str)
|
||||
{
|
||||
unsigned long samples = timeStamp() - m_tsStart;
|
||||
|
@ -454,6 +527,12 @@ void AnalyzerCons::statusParams(String& str)
|
|||
utime = utime ? ((1000000 * (u_int64_t)samples) + (utime / 2)) / utime : 0;
|
||||
str << ",rate=" << (unsigned int)utime;
|
||||
}
|
||||
if (m_total > 0) {
|
||||
double q = m_valid * 100.0 / m_total;
|
||||
char buf[64];
|
||||
snprintf(buf,sizeof(buf)-1,"quality=%0.2f",q);
|
||||
str.append(buf,",");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -478,7 +557,7 @@ AnalyzerChan::~AnalyzerChan()
|
|||
str.append("totaltime=",",") << buf;
|
||||
if (cons)
|
||||
cons->statusParams(str);
|
||||
Output("Analyzer %s finished: %s",id().c_str(),str.c_str());
|
||||
Output("Finished '%s' status: %s",id().c_str(),str.c_str());
|
||||
Engine::enqueue(message("chan.hangup"));
|
||||
}
|
||||
|
||||
|
@ -556,7 +635,7 @@ void AnalyzerChan::addSource()
|
|||
{
|
||||
if (getSource())
|
||||
return;
|
||||
const char* src = "tone/dial";
|
||||
const char* src = "tone/probe";
|
||||
if (m_address.startsWith("tone/"))
|
||||
src = m_address;
|
||||
Message m("chan.attach");
|
||||
|
@ -600,6 +679,7 @@ bool AnalyzerDriver::startCall(NamedList& params, const String& dest)
|
|||
if (tmp.null())
|
||||
tmp << prefix() << dest;
|
||||
m->addParam("caller",tmp);
|
||||
params.setParam("id",ac->id());
|
||||
return ac->startRouter(m);
|
||||
}
|
||||
|
||||
|
|
|
@ -325,6 +325,7 @@ bool MOHHandler::received(Message &msg)
|
|||
m.setParam("id",mc->id());
|
||||
m.userData(mc);
|
||||
if (Engine::dispatch(m)) {
|
||||
msg.setParam("id",mc->id());
|
||||
mc->deref();
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@ static const char s_helpmsg[] =
|
|||
" help [command]\n"
|
||||
" status [module]\n"
|
||||
" machine [on|off]\n"
|
||||
" output [on|off]\n"
|
||||
" auth password\n"
|
||||
"Authenticated commands:\n"
|
||||
" debug [level|on|off]\n"
|
||||
|
@ -84,6 +85,7 @@ public:
|
|||
private:
|
||||
bool m_auth;
|
||||
bool m_debug;
|
||||
bool m_output;
|
||||
bool m_machine;
|
||||
Socket* m_socket;
|
||||
String m_address;
|
||||
|
@ -158,7 +160,8 @@ Connection *Connection::checkCreate(Socket* sock, const char* addr)
|
|||
|
||||
Connection::Connection(Socket* sock, const char* addr)
|
||||
: Thread("RManager Connection"),
|
||||
m_auth(false), m_debug(false), m_machine(false), m_socket(sock), m_address(addr)
|
||||
m_auth(false), m_debug(false), m_output(true), m_machine(false),
|
||||
m_socket(sock), m_address(addr)
|
||||
{
|
||||
s_mutex.lock();
|
||||
connectionlist.append(this);
|
||||
|
@ -168,6 +171,7 @@ Connection::Connection(Socket* sock, const char* addr)
|
|||
Connection::~Connection()
|
||||
{
|
||||
m_debug = false;
|
||||
m_output = false;
|
||||
s_mutex.lock();
|
||||
connectionlist.remove(this,false);
|
||||
s_mutex.unlock();
|
||||
|
@ -283,6 +287,14 @@ bool Connection::processLine(const char *line)
|
|||
writeStr(str);
|
||||
return false;
|
||||
}
|
||||
else if (str.startSkip("output"))
|
||||
{
|
||||
str >> m_output;
|
||||
str = "Output mode: ";
|
||||
str += (m_output ? "on\n" : "off\n");
|
||||
writeStr(str);
|
||||
return false;
|
||||
}
|
||||
else if (str.startSkip("quit"))
|
||||
{
|
||||
writeStr(m_machine ? "%%=quit\n" : "Goodbye!\n");
|
||||
|
@ -360,8 +372,13 @@ bool Connection::processLine(const char *line)
|
|||
m.addParam("callto",str.substr(0,pos));
|
||||
m.addParam((target.find('/') > 0) ? "direct" : "target",target);
|
||||
|
||||
if (Engine::dispatch(m))
|
||||
str = (m_machine ? "%%=call:success:" : "Called ") + str + "\n";
|
||||
if (Engine::dispatch(m)) {
|
||||
String id(m.getValue("id"));
|
||||
if (m_machine)
|
||||
str = "%%=call:success:" + id + ":" + str + "\n";
|
||||
else
|
||||
str = "Calling '" + id + "' " + str + "\n";
|
||||
}
|
||||
else
|
||||
str = (m_machine ? "%%=call:fail:" : "Could not call ") + str + "\n";
|
||||
writeStr(str);
|
||||
|
@ -460,7 +477,9 @@ void Connection::writeStr(const Message &msg, bool received)
|
|||
|
||||
void Connection::writeDebug(const char *str, int level)
|
||||
{
|
||||
if (m_debug && !null(str))
|
||||
if (null(str))
|
||||
return;
|
||||
if (m_debug || (m_output && (level < 0)))
|
||||
writeStr(str,::strlen(str));
|
||||
}
|
||||
|
||||
|
|
|
@ -252,9 +252,9 @@ static const ToneDesc s_desc[] = {
|
|||
{ t_dtmf[13], "dtmf/b", "b" },
|
||||
{ t_dtmf[14], "dtmf/c", "c" },
|
||||
{ t_dtmf[15], "dtmf/d", "d" },
|
||||
{ t_probes[0], "probe/1", "test1" },
|
||||
{ t_probes[1], "probe/2", "test2" },
|
||||
{ t_probes[2], "probe/3", "test3" },
|
||||
{ t_probes[0], "probe/0", "probe" },
|
||||
{ t_probes[1], "probe/1", 0 },
|
||||
{ t_probes[2], "probe/2", 0 },
|
||||
{ 0, 0, 0 }
|
||||
};
|
||||
|
||||
|
@ -666,9 +666,10 @@ bool ToneGenDriver::msgExecute(Message& msg, String& dest)
|
|||
m = "call.execute";
|
||||
m.addParam("callto",callto);
|
||||
ToneChan *tc = new ToneChan(dest);
|
||||
m.setParam("targetid",tc->id());
|
||||
m.setParam("id",tc->id());
|
||||
m.userData(tc);
|
||||
if (Engine::dispatch(m)) {
|
||||
msg.setParam("id",tc->id());
|
||||
tc->deref();
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -716,9 +716,10 @@ bool WaveFileDriver::msgExecute(Message& msg, String& dest)
|
|||
m = "call.execute";
|
||||
m.addParam("callto",callto);
|
||||
WaveChan *c = new WaveChan(dest.matchString(2),meth,maxlen);
|
||||
m.setParam("targetid",c->id());
|
||||
m.setParam("id",c->id());
|
||||
m.userData(c);
|
||||
if (Engine::dispatch(m)) {
|
||||
msg.setParam("id",c->id());
|
||||
c->deref();
|
||||
return true;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue