Fixed data modulation issues.
git-svn-id: http://voip.null.ro/svn/yate@1559 acf43c95-373e-0410-b603-e72c3f656dc1
This commit is contained in:
parent
bc65a48206
commit
3884f0d36e
|
@ -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: */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue