Fixed data modulation issues.

git-svn-id: http://voip.null.ro/svn/yate@1559 acf43c95-373e-0410-b603-e72c3f656dc1
This commit is contained in:
marian 2007-12-18 17:23:07 +00:00
parent bc65a48206
commit 3884f0d36e
3 changed files with 155 additions and 79 deletions

View File

@ -29,12 +29,15 @@
using namespace TelEngine;
// Amplitudes for the sine generator (mark and space)
// Amplitudes for the sine generator (mark and space) used to modulate data
#define MARK_AMPLITUDE 6300
#define SPACE_AMPLITUDE 6300
// Pattern length in ms to add after a modulated message
#define PATTERN_AFTER 2
// Uncomment this to view the bits decoded by the modem
#define YMODEM_BUFFER_BITS
//#define YMODEM_BUFFER_BITS
// Constant values used by the FSK filter to modulate/demodulate data
class FilterConst
@ -54,10 +57,11 @@ public:
return tmp;
}
// Signal properties
float markFreq; // Mark frequency
float spaceFreq; // Space frequency
float sampleRate; // Sampling rate
float baudRate; // Transmission baud rate
float baudRate; // Transmission baud rate (bps)
// Data used to demodulate signals
unsigned int spb; // The number of samples per bit (also the length of all buffers)
unsigned int halfSpb; // Half of the spb value (used to filter data)
@ -70,11 +74,15 @@ public:
float* space;
float* lowband;
// Data used to modulate signals
unsigned int* bitSamples; // Array of bit samples nedded for to maintain the modulation timing
double accSin; // Accumulate sine radians during modulation
// This value is updated after the header is modulated
// and used as start value for each message data
unsigned int* bitSamples; // Array of bit samples nedded to maintain the modulation timing
unsigned int bitSamplesLen; // The length of the bitSamples array
double markCoef; // Mark coefficient for modulation
double spaceCoef; // Space coefficient for modulation
DataBlock header; // Message header if any (e.g. ETSI: channel seizure pattern + marks)
DataBlock header; // Start pattern + message header
// e.g. ETSI: channel seizure pattern + marks
};
struct FilterData
@ -172,7 +180,8 @@ public:
}
// Modulate data to a buffer. Reset the destination's length
// dataBits must not be 0 or greater then 8
void addBuffer(DataBlock& dest, const DataBlock& src, unsigned char dataBits, bool full);
// Returns the current sine accumulator value
double addBuffer(DataBlock& dest, const DataBlock& src, unsigned char dataBits, bool full);
private:
// Apply mark, space and low band filter
float filter(short*& samples, unsigned int& len);
@ -229,6 +238,7 @@ FilterConst::FilterConst(FSKModem::Type type)
spaceFreq = 2200.0;
sampleRate = 8000.0;
baudRate = 1200.0;
spb = 7;
halfSpb = spb / 2;
bitLen = sampleRate / baudRate;
@ -246,7 +256,7 @@ FilterConst::FilterConst(FSKModem::Type type)
lowband[i] = l[i];
}
// Build the array of bit samples nedded for to maintain the modulation timing
// Build the array of bit samples nedded to maintain the modulation timing
bitSamplesLen = 3;
bitSamples = new unsigned int[bitSamplesLen];
bitSamples[0] = bitSamples[2] = 7;
@ -256,7 +266,8 @@ FilterConst::FilterConst(FSKModem::Type type)
markCoef = 2 * M_PI * markFreq / sampleRate;
spaceCoef = 2 * M_PI * spaceFreq / sampleRate;
// Build header
accSin = 0;
// Build message header
// ETSI channel seizure signal + Mark (stop bits) signal
// 300 continuous bits of alternating 0 and 1 + 180 of 1 (mark) bits
// 480 bits: 60 bytes. Byte 38: 01010000
@ -265,10 +276,11 @@ FilterConst::FilterConst(FSKModem::Type type)
::memset(hdr,0x55,37);
::memset(&hdr[37],0x50,1);
::memset(&hdr[38],0xff,22);
DataBlock src(hdr,60,false);
DataBlock src;
FSKModem::addRaw(src,hdr,60);
FSKFilter filter(type);
filter.addBuffer(header,src,8,false);
src.clear(false);
// Keep the sine accumulator to be used when modulating data
accSin = filter.addBuffer(header,src,8,false);
Debug(s_libName,DebugInfo,"Initialized filter tables for type '%s' headerlen=%u",
lookup(FSKModem::ETSI,FSKModem::s_typeName),header.length());
@ -349,6 +361,8 @@ FSKFilter::FSKFilter(int type)
m_index = 0;
m_const = s_filterConst + type;
// Update the sine accumulator from constants (current value after created the header)
m_accSin = m_const->accSin;
m_mark.init(m_const->spb);
m_space.init(m_const->spb);
m_lowband.init(m_const->spb);
@ -435,7 +449,8 @@ inline void FSKFilter::addBit(short* samples, unsigned int& index, bool bit)
// Modulate data to a buffer
// dataBits must not be 0 or greater then 8
void FSKFilter::addBuffer(DataBlock& dest, const DataBlock& src,
// full=true: add data bytes (start bit + data + stop bit)
double FSKFilter::addBuffer(DataBlock& dest, const DataBlock& src,
unsigned char dataBits, bool full)
{
// Calculate the destination length. Add 2 more bits if full
@ -447,7 +462,7 @@ void FSKFilter::addBuffer(DataBlock& dest, const DataBlock& src,
else
dest.clear();
if (!dest.length())
return;
return m_accSin;
// Build modulated buffer
unsigned char* srcData = (unsigned char*)(src.data());
@ -459,6 +474,7 @@ void FSKFilter::addBuffer(DataBlock& dest, const DataBlock& src,
else
for (unsigned int i = 0; i < src.length(); i++)
addByte(destData,index,srcData[i],dataBits);
return m_accSin;
}
// Apply mark/space and low band filter
@ -649,7 +665,7 @@ bool FSKModem::demodulate(const DataBlock& data)
}
// Create a buffer containing the modulated representation of a message
void FSKModem::modulate(DataBlock& dest, const DataBlock& data, const DataBlock* header)
void FSKModem::modulate(DataBlock& dest, const DataBlock& data)
{
#ifdef DEBUG
String tmp;
@ -659,20 +675,20 @@ void FSKModem::modulate(DataBlock& dest, const DataBlock& data, const DataBlock*
if (!(data.length() && m_filter && m_filter->constants()))
return;
DataBlock tmpData;
m_filter->addBuffer(tmpData,data,m_uart->accumulator().dataBits(),true);
DDebug(m_uart,DebugAll,"Created %u modulated data buffer [%p]",
tmpData.length(),m_uart);
if (tmpData.length()) {
if (!header) {
dest = m_filter->constants()->header;
DDebug(m_uart,DebugAll,"Added %u default modulated header buffer [%p]",
dest.length(),m_uart);
}
else
m_filter->addBuffer(dest,data,m_uart->accumulator().dataBits(),false);
dest += tmpData;
}
dest += m_filter->constants()->header;
dest += tmpData;
// Build pattern after
unsigned int nbits = (unsigned int)(m_filter->constants()->baudRate / 1000 * PATTERN_AFTER);
DataBlock p(0,(nbits+7)/8);
DataBlock tmpAfter;
m_filter->addBuffer(tmpAfter,p,m_uart->accumulator().dataBits(),false);
dest += tmpAfter;
DDebug(m_uart,DebugAll,"Modulated header=%u data=%u pattern=%u [%p]",
m_filter->constants()->header.length(),
tmpData.length(),tmpAfter.length(),m_uart);
}
/* vi: set ts=8 sw=4 sts=4 noet: */

View File

@ -24,6 +24,8 @@
#include "yatemodem.h"
#include <string.h>
using namespace TelEngine;
// ETSI EN 300 659-1: 5.2
@ -33,17 +35,36 @@ using namespace TelEngine;
// Convert a buffer to a short
inline short net2short(unsigned char* buffer)
static inline short net2short(unsigned char* buffer)
{
return (buffer[0] << 8) | buffer[1];
}
// Append a buffer to a data block
inline void appendData(DataBlock& dest, void* buf, unsigned int len)
// Get date and time from system of received param
// dt: month,day,hour,minute;
// Return false if invalid
static bool getDateTime(unsigned char dt[4], String* src = 0, char sep = ':')
{
DataBlock tmp(buf,len,false);
dest += tmp;
tmp.clear(false);
static int minDt[4] = {1,1,0,0};
static int maxDt[4] = {12,31,23,59};
if (!src) {
// TODO: implement from system time
return false;
}
ObjList* list = src->split(sep);
int i = 0;
for (; i < 4; i++) {
String* s = static_cast<String*>((*list)[i]);
int tmp = s ? s->toInteger(-1) : -1;
if (tmp >= minDt[i] && tmp <= maxDt[i])
dt[i] = (unsigned char)tmp;
else
i = 5;
}
delete list;
return (i == 4);
}
@ -477,38 +498,44 @@ bool ETSIModem::decode(MsgType msg, const DataBlock& buffer)
// Append a parameter to a buffer
// Truncate it or set error if fail is true and parameter length exceeds maxLen
// Return 0 if the parameter is missing
int appendParam(ObjList& msg, NamedList& params, const char* name,
unsigned char value, unsigned char maxLen, bool fail)
// Return: 0 if the parameter is missing
// -1 if the parameter is too long
// 1 on success
int appendParam(ObjList& msg, NamedList& params, unsigned char value,
unsigned char maxLen, bool fail)
{
String tmp = params.getValue(name);
if (!tmp)
NamedString* ns = params.getParam(lookup(value,ETSIModem::s_msgParams));
if (!ns)
return 0;
unsigned char len = tmp.length() <= maxLen ? tmp.length() : maxLen;
if (len != tmp.length() && fail) {
params.setParam("error",String(name) + String("-too-long"));
return -1;
unsigned char len = ns->length();
if (len > maxLen) {
if (fail) {
params.setParam("error",ns->name() + "-too-long");
return -1;
}
len = maxLen;
}
DataBlock* data = new DataBlock;
unsigned char a[2] = {value,len};
appendData(*data,a,sizeof(a));
appendData(*data,(void*)tmp.c_str(),len);
FSKModem::addRaw(*data,a,sizeof(a));
FSKModem::addRaw(*data,(void*)ns->c_str(),len);
msg.append(data);
return 1;
}
// Append a parameter to a buffer from a list or dictionary
void appendParam(ObjList& msg, NamedList& params, const char* name,
unsigned char value, TokenDict* dict, unsigned char defValue)
void appendParam(ObjList& msg, NamedList& params, unsigned char value,
TokenDict* dict, unsigned char defValue)
{
unsigned char a[3] = {value,1};
const char* name = lookup(value,ETSIModem::s_msgParams);
a[2] = lookup(params.getValue(name),dict,defValue);
msg.append(new DataBlock(a,sizeof(a)));
}
// Create a buffer containing the byte representation of a message to be sent
// and another one with the header
bool ETSIModem::createMsg(NamedList& params, DataBlock& data, DataBlock*& header)
bool ETSIModem::createMsg(NamedList& params, DataBlock& data)
{
int type = lookup(params,s_msg);
switch (type) {
@ -517,55 +544,79 @@ bool ETSIModem::createMsg(NamedList& params, DataBlock& data, DataBlock*& header
case MsgMWI:
case MsgCharge:
case MsgSMS:
Debug(this,DebugStub,"Create message '%s' not implemented [%p]",params.c_str(),this);
Debug(this,DebugStub,"Create message '%s' not implemented [%p]",
params.c_str(),this);
return false;
default:
Debug(this,DebugNote,"Can't create unknown message '%s' [%p]",params.c_str(),this);
Debug(this,DebugNote,"Can't create unknown message '%s' [%p]",
params.c_str(),this);
return false;
}
ObjList msg;
bool fail = !params.getBoolValue("force-send",true);
bool datetime = params.getBoolValue("datetime",true);
if (datetime) {
// TODO: set date and time. Len: 8
// "%.2d%.2d%.2d%.2d month:day:hour:minute
// DateTime - ETSI EN 300 659-3 - 5.4.1
String datetime = params.getValue("datetime");
unsigned char dt[4];
bool ok = false;
if (datetime.isBoolean())
if (datetime.toBoolean())
ok = getDateTime(dt);
else ;
else
ok = getDateTime(dt,&datetime);
if (ok) {
DataBlock* dtParam = new DataBlock(0,10);
unsigned char* d = (unsigned char*)dtParam->data();
d[0] = DateTime;
d[1] = 8;
// Set date and time: %.2d%.2d%.2d%.2d month:day:hour:minute
for (int i = 0, j = 2; i < 4; i++, j += 2) {
d[j] = '0' + dt[i] / 10;
d[j+1] = '0' + dt[i] % 10;
}
msg.append(dtParam);
}
else
DDebug(this,DebugInfo,"Can't set datetime parameter from '%s' [%p]",
datetime.c_str(),this);
// ETSI EN 300 659-3 - 5.4.2: Max caller id 20
int res = appendParam(msg,params,"caller",CallerId,20,fail);
if (res == -1)
return false;
// Default caller absence: 0x4f: unavailable
if (!res)
appendParam(msg,params,"callerpres",CallerIdReason,s_dict_callerAbsence,0x4f);
// ETSI EN 300 659-3 - 5.4.5: Max caller name 50
res = appendParam(msg,params,"callername",CallerName,50,fail);
// CallerId/CallerIdReason - ETSI EN 300 659-3 - 5.4.2: Max caller id 20
// Parameter is missing: append reason (default caller absence: 0x4f: unavailable)
int res = appendParam(msg,params,CallerId,20,fail);
if (res == -1)
return false;
if (!res)
appendParam(msg,params,"callerpres",CallerNameReason,s_dict_callerAbsence,0x4f);
appendParam(msg,params,CallerIdReason,s_dict_callerAbsence,0x4f);
// CallerName/CallerNameReason - ETSI EN 300 659-3 - 5.4.5: Max caller name 50
// Parameter is missing: append reason (default callername absence: 0x4f: unavailable)
res = appendParam(msg,params,CallerName,50,fail);
if (res == -1)
return false;
if (!res)
appendParam(msg,params,CallerNameReason,s_dict_callerAbsence,0x4f);
// Build message
unsigned char len = 0;
unsigned char hdr[2] = {type};
data.assign(&hdr,sizeof(hdr));
for (ObjList* o = msg.skipNull(); o; o = o->skipNext()) {
DataBlock* msgParam = static_cast<DataBlock*>(o->get());
if (len + msgParam->length() > 255) {
if (!fail)
if (!fail) {
Debug(this,DebugNote,"Trucating %s message length to %u bytes [%p]",
params.c_str(),data.length(),this);
break;
}
params.setParam("error","message-too-long");
return false;
}
len += msgParam->length();
data += *msgParam;
}
if (!len) {
params.setParam("error","empty-message");
return false;
@ -577,7 +628,7 @@ bool ETSIModem::createMsg(NamedList& params, DataBlock& data, DataBlock*& header
for (unsigned int i = 0; i < data.length(); i++)
m_chksum += buf[i];
unsigned char crcVal = 256 - (m_chksum & 0xff);
appendData(data,&crcVal,1);
FSKModem::addRaw(data,&crcVal,1);
return true;
}

View File

@ -180,14 +180,26 @@ public:
bool demodulate(const DataBlock& data);
/**
* Create a buffer containing the modulated representation of a message
* Create a buffer containing the modulated representation of a message.
* A data pattern (depending on modem's type) will be added before the message.
* A mark pattern (2ms long) will be added after the message.
* Reset the modem before each request to modulate
* @param dest Destination buffer
* @param data Message data (each byte will be enclosed in start/stop/parity bits)
* @param header Optional bit pattern to be used as message header. This is the
* bit representation of the header, not modulated data. A default one will be
* added if header is missing
*/
void modulate(DataBlock& dest, const DataBlock& data, const DataBlock* header = 0);
void modulate(DataBlock& dest, const DataBlock& data);
/**
* Append a raw buffer to a data block
* @param dest Destination buffer
* @param buf Buffer to append to destination
* @param len the number of bytes to append starting with buf
*/
static inline void addRaw(DataBlock& dest, void* buf, unsigned int len) {
DataBlock tmp(buf,len,false);
dest += tmp;
tmp.clear(false);
}
/**
* Keep the modem type names. Useful to configure the modem
@ -293,10 +305,9 @@ public:
*/
inline bool modulate(DataBlock& dest, NamedList& params) {
DataBlock data;
DataBlock* header = 0;
if (!createMsg(params,data,header))
if (!createMsg(params,data))
return false;
m_modem.modulate(dest,data,header);
m_modem.modulate(dest,data);
return true;
}
@ -348,10 +359,9 @@ protected:
* Create a buffer containing the byte representation of a message to be sent
* @param params The list containing message parameters
* @param data Destination message data buffer
* @param header Destination message header buffer
* @return False on failure
*/
virtual bool createMsg(NamedList& params, DataBlock& data, DataBlock*& header)
virtual bool createMsg(NamedList& params, DataBlock& data)
{ return false; }
/**
@ -569,10 +579,9 @@ protected:
* @param params The list containing message parameters.
* The name of the list must be a valid (known) message
* @param data Destination message data buffer
* @param header Destination message header buffer
* @return False on failure (an 'error' parameter will be set in params)
*/
virtual bool createMsg(NamedList& params, DataBlock& data, DataBlock*& header);
virtual bool createMsg(NamedList& params, DataBlock& data);
private:
// Change decoder's state