* 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 development
pull/1/head
David Freese 2011-08-30 14:09:00 -05:00
rodzic ae579b49d1
commit ff3b0b2039
7 zmienionych plików z 320 dodań i 97 usunięć

Wyświetl plik

@ -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 \

203
src/dtmf/dtmf.cxx 100644
Wyświetl plik

@ -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();
}

107
src/include/dtmf.h 100644
Wyświetl plik

@ -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();
};

Wyświetl plik

@ -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);

Wyświetl plik

@ -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"));

Wyświetl plik

@ -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();
}

Wyświetl plik

@ -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