kopia lustrzana https://github.com/jamescoxon/dl-fldigi
DTMF class
* Changed implementation to separate class, similar to RsID implementation * Added Delay and Tone Pulse Length parameters to <DTMF: tag - <DTMF:D500:L250:1-256-828-3105> - | | |_phone # - | |_250 msec pulse duration, 50 msec default - |_500 msec wait before first tone, 0 default * Decoder under developmentpull/1/head
rodzic
ae579b49d1
commit
ff3b0b2039
|
@ -252,6 +252,7 @@ fldigi_SOURCES += \
|
|||
dialogs/Viewer.cxx \
|
||||
dialogs/htmlstrings.cxx \
|
||||
dialogs/notifydialog.cxx \
|
||||
dtmf/dtmf.cxx \
|
||||
thor/thor.cxx \
|
||||
thor/thorvaricode.cxx \
|
||||
dominoex/dominoex.cxx \
|
||||
|
@ -269,6 +270,7 @@ fldigi_SOURCES += \
|
|||
include/htmlstrings.h \
|
||||
include/arq_io.h \
|
||||
include/confdialog.h \
|
||||
include/dtmf.h \
|
||||
include/FTextView.h \
|
||||
include/FTextRXTX.h \
|
||||
include/fileselect.h \
|
||||
|
|
|
@ -0,0 +1,203 @@
|
|||
// ----------------------------------------------------------------------------
|
||||
//
|
||||
// DTMF.cxx
|
||||
//
|
||||
// Copyright (C) 2011
|
||||
// Dave Freese, W1HKJ
|
||||
//
|
||||
// This file is part of fldigi.
|
||||
//
|
||||
// Fldigi is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Fldigi is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with fldigi. If not, see <http://www.gnu.org/licenses/>.
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <float.h>
|
||||
#include <samplerate.h>
|
||||
|
||||
#include "dtmf.h"
|
||||
#include "filters.h"
|
||||
#include "fft.h"
|
||||
#include "misc.h"
|
||||
#include "trx.h"
|
||||
#include "waterfall.h"
|
||||
|
||||
#include "fl_digi.h"
|
||||
#include "configuration.h"
|
||||
#include "confdialog.h"
|
||||
#include "qrunner.h"
|
||||
#include "notify.h"
|
||||
#include "debug.h"
|
||||
|
||||
#include "main.h"
|
||||
|
||||
LOG_FILE_SOURCE(debug::LOG_MODEM);
|
||||
|
||||
// tones in 4x4 array
|
||||
// 697 770 842 941 1209 1336 1447 1633
|
||||
|
||||
int cDTMF::tones[] = {697, 770, 842, 941, 1209, 1336, 1477, 1633};
|
||||
|
||||
int cDTMF::row[] = {
|
||||
941, 697, 697, 697, 770, 770, 770, 852, 852, 852, 941, 941, 697, 770, 852, 941};
|
||||
//0, 1, 2, 3, 4, 5, 6, 7, 8, 9, *, #, A, B, C, D
|
||||
|
||||
int cDTMF::col[] = {
|
||||
1336, 1209, 1336, 1477, 1209, 1336, 1477, 1209, 1336, 1477, 1209, 1477, 1633, 1633, 1633, 1633};
|
||||
// 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, *, #, A, B, C, D
|
||||
|
||||
cDTMF::PAIRS cDTMF::pairs[] = {
|
||||
{0, 0, '1'}, {0, 1, '2'}, {0, 2, '3'}, {0, 3, 'A'},
|
||||
{1, 0, '4'}, {1, 1, '5'}, {1, 2, '6'}, {1, 3, 'B'},
|
||||
{2, 0, '7'}, {2, 1, '8'}, {2, 2, '9'}, {2, 3, 'C'},
|
||||
{3, 0, '*'}, {3, 1, '0'}, {3, 2, '#'}, {3, 3, 'D'} };
|
||||
|
||||
void cDTMF::makeshape()
|
||||
{
|
||||
for (int i = 0; i < 128; i++) shape[i] = 1.0;
|
||||
for (int i = 0; i < RT; i++)
|
||||
shape[i] = 0.5 * (1.0 - cos (M_PI * i / RT));
|
||||
}
|
||||
|
||||
|
||||
cDTMF::cDTMF()
|
||||
{
|
||||
}
|
||||
|
||||
//rx is a work in progress :>)
|
||||
|
||||
void cDTMF::receive(const float* buf, size_t len)
|
||||
{
|
||||
int binlo = 0, binhi = 0;
|
||||
double val1 = 0, val2 = 0, avg = 1e-6;
|
||||
//this doesn't work!
|
||||
for (int i = 0; i < 8; i++) {
|
||||
bins[i] = wf->powerDensity(tones[i], 4);
|
||||
avg += bins[i] / 8;
|
||||
}
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (bins[i] > val1) { val1 = bins[i]; binlo = i; }
|
||||
if (bins[i+4] > val2) { val2 = bins[i+4]; binhi = i;}
|
||||
}
|
||||
if ((val1 / avg > 0.5) && (val2 / avg > 0.5)) {
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (pairs[i].lo == binlo && pairs[i].hi == binhi) {
|
||||
printf("DTMF %c\n", pairs[i].ch);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//======================================================================
|
||||
// DTMF tone transmit
|
||||
//======================================================================
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// transmit silence for specified time duration in milliseconds
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void cDTMF::silence(int len)
|
||||
{
|
||||
double sr = active_modem->get_samplerate();
|
||||
int length = len * sr / 1000;
|
||||
if (length > 16384) length = 16384;
|
||||
memset(outbuf, 0, length * sizeof(*outbuf));
|
||||
active_modem->ModulateXmtr(outbuf, length);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// transmit DTMF tones for specific time interval
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void cDTMF::two_tones(int rc)
|
||||
{
|
||||
if (rc < 0 || rc > 15) return;
|
||||
double sr = active_modem->get_samplerate();
|
||||
double phaseincr = 2.0 * M_PI * row[rc] / sr;
|
||||
double phase = 0;
|
||||
int length = duration * sr / 1000;
|
||||
if (length > 16384) length = 16384;
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
outbuf[i] = 0.5 * sin(phase);
|
||||
phase += phaseincr;
|
||||
}
|
||||
|
||||
phaseincr = 2.0 * M_PI * col[rc] / sr;
|
||||
phase = 0;
|
||||
for (int i = 0; i < length; i++) {
|
||||
outbuf[i] += 0.5 * sin(phase);
|
||||
phase += phaseincr;
|
||||
}
|
||||
for (int i = 0; i < RT; i++) {
|
||||
outbuf[i] *= shape[i];
|
||||
outbuf[length - 1 - i] *= shape[i];
|
||||
}
|
||||
|
||||
active_modem->ModulateXmtr(outbuf, length);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// transmit the string contained in progdefaults.DTMFstr and output as
|
||||
// dtmf valid characters, 0-9, *, #, A-D
|
||||
// each pair of tones is separated by 50ms silence
|
||||
// 500 msec silence for ' ', ',' or '-'
|
||||
// 50 msec silence for invalid characters
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void cDTMF::send()
|
||||
{
|
||||
if (progdefaults.DTMFstr.empty()) return;
|
||||
|
||||
int c = 0, delay = 0;
|
||||
duration = 50;
|
||||
RT = (int)(active_modem->get_samplerate() * 4 / 1000.0); // 4 msec edge
|
||||
makeshape();
|
||||
size_t colon = std::string::npos;
|
||||
|
||||
size_t modifier = progdefaults.DTMFstr.find('D');
|
||||
if (modifier != std::string::npos) {
|
||||
delay = atoi(&progdefaults.DTMFstr[modifier + 1]);
|
||||
colon = progdefaults.DTMFstr.find(':', modifier);
|
||||
progdefaults.DTMFstr.erase(modifier, colon - modifier + 1);
|
||||
}
|
||||
|
||||
modifier = progdefaults.DTMFstr.find('L');
|
||||
if (modifier != std::string::npos) {
|
||||
duration = atoi(&progdefaults.DTMFstr[modifier + 1]);
|
||||
colon = progdefaults.DTMFstr.find(':', modifier);
|
||||
progdefaults.DTMFstr.erase(modifier, colon - modifier + 1);
|
||||
}
|
||||
|
||||
while (delay > 50) { silence(50); delay -= 50;}
|
||||
if (delay) silence(delay);
|
||||
|
||||
for(size_t i = 0; i < progdefaults.DTMFstr.length(); i++) {
|
||||
c = progdefaults.DTMFstr[i];
|
||||
if (c == ' ' || c == ',' || c == '-')
|
||||
silence(duration);
|
||||
else if (c >= '0' && c <= '9') two_tones(c - '0');
|
||||
else if (c == '*') two_tones(10);
|
||||
else if (c == '#') two_tones(11);
|
||||
else if (c >= 'A' && c <= 'D') two_tones(12 + c - 'A');
|
||||
else if (c >= 'a' && c <= 'd') two_tones(12 + c - 'a');
|
||||
silence(duration);
|
||||
}
|
||||
progdefaults.DTMFstr.clear();
|
||||
}
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
#include "filters.h"
|
||||
|
||||
// #define X1 0 /* 350 dialtone */
|
||||
// #define X2 1 /* 440 ring, dialtone */
|
||||
// #define X3 2 /* 480 ring, busy */
|
||||
// #define X4 3 /* 620 busy */
|
||||
|
||||
// #define R1 4 /* 697, dtmf row 1 */
|
||||
// #define R2 5 /* 770, dtmf row 2 */
|
||||
// #define R3 6 /* 852, dtmf row 3 */
|
||||
// #define R4 8 /* 941, dtmf row 4 */
|
||||
// #define C1 10 /* 1209, dtmf col 1 */
|
||||
// #define C2 12 /* 1336, dtmf col 2 */
|
||||
// #define C3 13 /* 1477, dtmf col 3 */
|
||||
// #define C4 14 /* 1633, dtmf col 4 */
|
||||
|
||||
// #define B1 4 /* 700, blue box 1 */
|
||||
// #define B2 7 /* 900, bb 2 */
|
||||
// #define B3 9 /* 1100, bb 3 */
|
||||
// #define B4 11 /* 1300, bb4 */
|
||||
// #define B5 13 /* 1500, bb5 */
|
||||
// #define B6 15 /* 1700, bb6 */
|
||||
// #define B7 16 /* 2400, bb7 */
|
||||
// #define B8 17 /* 2600, bb8 */
|
||||
|
||||
// #define NUMTONES 18
|
||||
|
||||
/* values returned by detect
|
||||
* 0-9 DTMF 0 through 9 or MF 0-9
|
||||
* 10-11 DTMF *, #
|
||||
* 12-15 DTMF A,B,C,D
|
||||
* 16-20 MF last column: C11, C12, KP1, KP2, ST
|
||||
* 21 2400
|
||||
* 22 2600
|
||||
* 23 2400 + 2600
|
||||
* 24 DIALTONE
|
||||
* 25 RING
|
||||
* 26 BUSY
|
||||
* 27 silence
|
||||
* -1 invalid
|
||||
*/
|
||||
/*
|
||||
#define D0 0
|
||||
#define D1 1
|
||||
#define D2 2
|
||||
#define D3 3
|
||||
#define D4 4
|
||||
#define D5 5
|
||||
#define D6 6
|
||||
#define D7 7
|
||||
#define D8 8
|
||||
#define D9 9
|
||||
#define DSTAR 10
|
||||
#define DPND 11
|
||||
#define DA 12
|
||||
#define DB 13
|
||||
#define DC 14
|
||||
#define DD 15
|
||||
#define DC11 16
|
||||
#define DC12 17
|
||||
#define DKP1 18
|
||||
#define DKP2 19
|
||||
#define DST 20
|
||||
#define D24 21
|
||||
#define D26 22
|
||||
#define D2426 23
|
||||
#define DDT 24
|
||||
#define DRING 25
|
||||
#define DBUSY 26
|
||||
#define DSIL 27
|
||||
*/
|
||||
/* translation of above codes into text */
|
||||
/*
|
||||
char *dtran[] = {
|
||||
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
|
||||
"*", "#", "A", "B", "C", "D",
|
||||
"+C11 ", "+C12 ", " KP1+", " KP2+", "+ST ",
|
||||
" 2400 ", " 2600 ", " 2400+2600 ",
|
||||
" DIALTONE ", " RING ", " BUSY ","" };
|
||||
*/
|
||||
//#define RANGE 0.1 /* any thing higher than RANGE*peak is "on" */
|
||||
//#define THRESH 100.0 /* minimum level for the loudest tone */
|
||||
//#define FLUSH_TIME 100 /* 100 frames = 3 seconds */
|
||||
|
||||
class cDTMF {
|
||||
struct PAIRS {int lo; int hi; char ch;};
|
||||
private:
|
||||
static int row[];
|
||||
static int col[];
|
||||
static int tones[];
|
||||
static PAIRS pairs[];
|
||||
double bins[8];
|
||||
double outbuf[16384];
|
||||
double shape[128];
|
||||
int RT;
|
||||
int duration;
|
||||
|
||||
public:
|
||||
cDTMF();
|
||||
~cDTMF() {}
|
||||
void receive(const float* buf, size_t len);
|
||||
void makeshape();
|
||||
void silence(int);
|
||||
void two_tones(int);
|
||||
void send();
|
||||
|
||||
};
|
|
@ -65,8 +65,6 @@ protected:
|
|||
bool s2n_valid;
|
||||
|
||||
unsigned cap;
|
||||
static int DTMF_row[];
|
||||
static int DTMF_col[];
|
||||
|
||||
public:
|
||||
modem();
|
||||
|
@ -180,11 +178,6 @@ public:
|
|||
void cwid_sendtext (const std::string& s);
|
||||
void cwid();
|
||||
|
||||
// for DTMF transmission
|
||||
void DTMF_silence(int);
|
||||
void DTMF_two_tones(int);
|
||||
void DTMF_send();
|
||||
|
||||
// for noise tests
|
||||
private:
|
||||
void add_noise(double *, int);
|
||||
|
|
|
@ -158,7 +158,7 @@ void loadBrowser(Fl_Widget *widget) {
|
|||
w->add(_("<TEXT>\tvideo text"));
|
||||
w->add(_("<TXRSID:on|off|t>\tTx RSID on,off,toggle"));
|
||||
w->add(_("<RXRSID:on|off|t>\tRx RSID on,off,toggle"));
|
||||
w->add(_("<DTMF:digits>\tTx digits string"));
|
||||
w->add(_("<DTMF:[Dnnn:][Lnnn:]digits>\t[Delay][Len](ms)"));
|
||||
|
||||
w->add(LINE_SEP);
|
||||
w->add(_("<POST:+/-nn.n>\tCW QSK post-timing"));
|
||||
|
|
|
@ -969,91 +969,3 @@ mfntchr idch2[] = {
|
|||
{'}', { 0x00, 0x80, 0xC0, 0x40, 0x40, 0x40, 0x60, 0x60, 0x40, 0x40, 0x40, 0xC0, 0x80, 0x00 }, },
|
||||
{'~', { 0x00, 0x98, 0xFC, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }
|
||||
};
|
||||
|
||||
|
||||
//======================================================================
|
||||
// DTMF tone transmit
|
||||
//======================================================================
|
||||
int modem::DTMF_row[] = {
|
||||
941, 697, 697, 697, 770, 770, 770, 852, 852, 852, 941, 941, 697, 770, 852, 941};
|
||||
//0, 1, 2, 3, 4, 5, 6, 7, 8, 9, *, #, A, B, C, D
|
||||
|
||||
int modem::DTMF_col[] = {
|
||||
1336, 1209, 1336, 1477, 1209, 1336, 1477, 1209, 1336, 1477, 1209, 1477, 1633, 1633, 1633, 1633};
|
||||
// 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, *, #, A, B, C, D
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// transmit silence for specified time duration
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void modem::DTMF_silence(int duration)
|
||||
{
|
||||
double sr = active_modem->get_samplerate();
|
||||
int length = duration * sr / 1000;
|
||||
memset(outbuf, 0, length * sizeof(*outbuf));
|
||||
active_modem->ModulateXmtr(outbuf, length);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// transmit DTMF tones for specific time interval
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void modem::DTMF_two_tones(int rc)
|
||||
{
|
||||
if (rc < 0 || rc > 15) return;
|
||||
double sr = active_modem->get_samplerate();
|
||||
double phaseincr = 2.0 * M_PI * DTMF_row[rc] / sr;
|
||||
double phase = 0;
|
||||
printf("%d %d\n", DTMF_row[rc], DTMF_col[rc]);
|
||||
int length = 50 * sr / 1000;
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
outbuf[i] = 0.5 * sin(phase);
|
||||
phase += phaseincr;
|
||||
}
|
||||
|
||||
phaseincr = 2.0 * M_PI * DTMF_col[rc] / sr;
|
||||
phase = 0;
|
||||
for (int i = 0; i < length; i++) {
|
||||
outbuf[i] += 0.5 * sin(phase);
|
||||
phase += phaseincr;
|
||||
}
|
||||
for (int i = 0; i < RT; i++) {
|
||||
outbuf[i] *= cwid_keyshape[i];
|
||||
outbuf[length - 1 - i] *= cwid_keyshape[i];
|
||||
}
|
||||
|
||||
active_modem->ModulateXmtr(outbuf, length);
|
||||
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// transmit the string contained in progdefaults.DTMFstr and output as
|
||||
// dtmf valid characters, 0-9, *, #, A-D
|
||||
// each pair of tones is separated by 50ms silence
|
||||
// 500 msec silence for ' ', ',' or '-'
|
||||
// 50 msec silence for invalid characters
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void modem::DTMF_send()
|
||||
{
|
||||
if (progdefaults.DTMFstr.empty()) return;
|
||||
|
||||
int c = 0;
|
||||
|
||||
RT = (int) (samplerate * 4 / 1000.0); // 4 msec rise/fall time
|
||||
cwid_makeshape();
|
||||
|
||||
for(size_t i = 0; i < progdefaults.DTMFstr.length(); i++) {
|
||||
c = progdefaults.DTMFstr[i];
|
||||
if (c == ' ' || c == ',' || c == '-') DTMF_silence(50);
|
||||
else if (c >= '0' && c <= '9') DTMF_two_tones(c - '0');
|
||||
else if (c == '*') DTMF_two_tones(10);
|
||||
else if (c == '#') DTMF_two_tones(11);
|
||||
else if (c >= 'A' && c <= 'D') DTMF_two_tones(12 + c - 'A');
|
||||
else if (c >= 'a' && c <= 'd') DTMF_two_tones(12 + c - 'a');
|
||||
DTMF_silence(50);
|
||||
}
|
||||
progdefaults.DTMFstr.clear();
|
||||
}
|
||||
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#include "misc.h"
|
||||
#include "configuration.h"
|
||||
#include "status.h"
|
||||
#include "dtmf.h"
|
||||
|
||||
#include "soundconf.h"
|
||||
#include "ringbuffer.h"
|
||||
|
@ -68,6 +69,7 @@ state_t trx_state;
|
|||
|
||||
modem *active_modem = 0;
|
||||
cRsId *ReedSolomon = 0;
|
||||
cDTMF *dtmf = 0;
|
||||
SoundBase *scard;
|
||||
static int _trx_tune;
|
||||
|
||||
|
@ -263,6 +265,7 @@ void trx_trx_receive_loop()
|
|||
active_modem->rx_process(rbvec[0].buf, numread);
|
||||
if (progdefaults.rsid)
|
||||
ReedSolomon->receive(fbuf, numread);
|
||||
// dtmf->receive(fbuf, numread);
|
||||
}
|
||||
else {
|
||||
bool afc = progStatus.afconoff;
|
||||
|
@ -312,7 +315,7 @@ void trx_trx_transmit_loop()
|
|||
if (progdefaults.TransmitRSid)
|
||||
ReedSolomon->send(true);
|
||||
|
||||
active_modem->DTMF_send();
|
||||
dtmf->send();
|
||||
|
||||
while (trx_state == STATE_TX) {
|
||||
try {
|
||||
|
@ -533,6 +536,7 @@ void trx_start(void)
|
|||
|
||||
if (scard) delete scard;
|
||||
if (ReedSolomon) delete ReedSolomon;
|
||||
if (dtmf) delete dtmf;
|
||||
|
||||
|
||||
switch (progdefaults.btnAudioIOis) {
|
||||
|
@ -559,6 +563,8 @@ void trx_start(void)
|
|||
}
|
||||
|
||||
ReedSolomon = new cRsId;
|
||||
dtmf = new cDTMF;
|
||||
|
||||
#endif // !BENCHMARK_MODE
|
||||
|
||||
#if USE_NAMED_SEMAPHORES
|
||||
|
|
Ładowanie…
Reference in New Issue