// Break received audio data into manageable chunks, forward them to decoder void FaxTerminal::rxData(const DataBlock& data, unsigned long tStamp) { unsigned int pos = 0; while (pos < data.length()) { // feed the decoder with small chunks of data (16 bytes/ms) int len = data.length() - pos; if (len > FAX_DATA_CHUNK) len = FAX_DATA_CHUNK; rxBlock(((char *)data.data())+pos, len); pos += len; } }
// Convert endiannes bool RadioDataFile::fixEndian(DataBlock& buf, unsigned int bytes) { if (!bytes) return false; unsigned int n = buf.length() / bytes; if (bytes == 2) { for (uint16_t* p = (uint16_t*)buf.data(); n; n--, p++) #ifdef LITTLE_ENDIAN *p = be16toh(*p); #else *p = le16toh(*p); #endif return true; } if (bytes == 4) { for (uint32_t* p = (uint32_t*)buf.data(); n; n--, p++) #ifdef LITTLE_ENDIAN *p = be32toh(*p); #else *p = le32toh(*p); #endif return true; } if (bytes == 8) { for (uint64_t* p = (uint64_t*)buf.data(); n; n--, p++) #ifdef LITTLE_ENDIAN *p = be64toh(*p); #else *p = le64toh(*p); #endif return true; } return false; }
virtual unsigned long Consume(const DataBlock& data, unsigned long tStamp, unsigned long flags) { if (!ref()) return 0; unsigned long len = 0; if (m_valid && getTransSource() && m_buffer.convert(data,m_sFmt,m_dFmt)) { if (tStamp == invalidStamp()) { unsigned int delta = data.length(); if (delta > m_buffer.length()) delta = m_buffer.length(); tStamp = m_timestamp + delta; } m_timestamp = tStamp; len = getTransSource()->Forward(m_buffer,tStamp,flags); } deref(); return len; }
// Feed samples to the filter(s) unsigned long ToneConsumer::Consume(const DataBlock& data, unsigned long tStamp, unsigned long flags) { unsigned int samp = data.length() / 2; if (m_mode != Mono) samp /= 2; if (!samp) return 0; const int16_t* s = (const int16_t*)data.data(); if (!s) return 0; while (samp--) { m_xv[0] = m_xv[1]; m_xv[1] = m_xv[2]; switch (m_mode) { case Left: // use 1st sample, skip 2nd m_xv[2] = *s++; s++; break; case Right: // skip 1st sample, use 2nd s++; m_xv[2] = *s++; break; case Mixed: // add together samples m_xv[2] = s[0]+(int)s[1]; s+=2; break; default: m_xv[2] = *s++; } double dx = m_xv[2] - m_xv[0]; updatePwr(m_pwr,m_xv[2]); // update all active detectors if (m_detFax) m_fax.update(dx); if (m_detCont) m_cont.update(dx); if (m_detDtmf || m_detDnis) { for (int j = 0; j < 4; j++) { m_dtmfL[j].update(dx); m_dtmfH[j].update(dx); } } // only do checks every millisecond if (samp % 8) continue; // is it enough total power to accept a signal? if (m_pwr >= THRESHOLD2_ABS) { if (m_detDtmf || m_detDnis) checkDtmf(); if (m_detFax) checkFax(); if (m_detCont) checkCont(); } else { m_dtmfTone = '\0'; m_dtmfCount = 0; } } XDebug(&plugin,DebugAll,"Fax detector on %s: signal=%0.1f, total=%0.1f", m_id.c_str(),m_fax.value(),m_pwr); return invalidStamp(); }
// 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) { int type = lookup(params,s_msg); switch (type) { case MsgCallSetup: break; case MsgMWI: case MsgCharge: case MsgSMS: 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); return false; } ObjList msg; bool fail = !params.getBoolValue("force-send",true); // 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); // 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,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) { 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; } unsigned char* buf = ((unsigned char*)(data.data())); buf[1] = len; m_chksum = 0; for (unsigned int i = 0; i < data.length(); i++) m_chksum += buf[i]; unsigned char crcVal = 256 - (m_chksum & 0xff); FSKModem::addRaw(data,&crcVal,1); return true; }
// Process (decode) a valid received buffer. Call recvParams() after decoding the message // Return false to stop processing data bool ETSIModem::decode(MsgType msg, const DataBlock& buffer) { NamedList params(""); DDebug(this,DebugAll,"Decoding message %s [%p]",lookup(msg,s_msg),this); unsigned char* data = (unsigned char*)buffer.data(); for (unsigned int i = 0; i < buffer.length();) { unsigned char param = data[i++]; // Param type const char* pname = lookup(param,s_msgParams); unsigned int len = data[i++]; // Param length (non 0) unsigned char* pdata = data + i; // End of buffer: Force index outside the end of buffer if (i < buffer.length()) i += data[i-1]; else i++; if (i > buffer.length()) { Debug(this,DebugWarn,"Unexpected end of %s parameter [%p]",pname,this); return UART::error(UART::EInvalidData); } String tmp; #define CHECK_LEN(expected) \ if (len != expected) { \ Debug(this,DebugNote,"Invalid len=%u (expected %u) for %s parameter [%p]",len,expected,pname,this); \ continue; \ } #define SET_PARAM_FROM_DATA(paramname) \ tmp.assign((char*)pdata,len); \ params.addParam(paramname,tmp); #define SET_PARAM_FROM_DICT(paramname,dict) \ tmp = lookup(*pdata,dict,"unknown"); \ params.addParam(paramname,tmp); // Process parameters // References are the sections from ETSI EN 300 659-3 switch (param) { case CallerId: // 5.4.2 SET_PARAM_FROM_DATA("caller") break; case CallerName: // 5.4.5 SET_PARAM_FROM_DATA("callername") break; case CallerIdReason: // 5.4.4 CHECK_LEN(1) SET_PARAM_FROM_DICT("callerpres",s_dict_callerAbsence) break; case CallerNameReason: // 5.4.6 CHECK_LEN(1) SET_PARAM_FROM_DICT("callernamepres",s_dict_callerAbsence) break; case DateTime: // 5.4.1 CHECK_LEN(8) setDateTime(tmp,(char*)pdata,8); params.addParam("datetime",tmp); break; case CompDateTime: // 5.4.10 if (param == CompDateTime && len != 8 && len != 10) { Debug(this,DebugNote, "Invalid len=%u (expected 8 or 10) for %s parameter [%p]",len,pname,this); continue; } setDateTime(tmp,(char*)pdata,len); params.addParam("service_datetime",tmp); break; case CalledId: // 5.4.3 SET_PARAM_FROM_DATA("called") break; case CallType: // 5.4.12 CHECK_LEN(1) SET_PARAM_FROM_DICT("calltype",s_dict_callType) break; case CallerType: // 5.4.16 CHECK_LEN(1) SET_PARAM_FROM_DICT("originator_type",s_dict_callerType) break; case VisualIndicator: // 5.4.7 CHECK_LEN(1) if (*pdata == 0 || *pdata == 255) tmp = String::boolText(*pdata != 0); else tmp = (int)(*pdata); params.addParam("visualindicator",tmp); break; case MessageId: // 5.4.8 CHECK_LEN(3) SET_PARAM_FROM_DICT("message_status",s_dict_mwiStatus) params.addParam("message_ref",String(net2short(pdata + 1))); DDebug(this,DebugInfo, "Decoded %s parameter (status=%s ref=%d) [%p]", pname,tmp.c_str(),net2short(pdata + 1),this); continue; case LastMsgCLI: // 5.4.9 SET_PARAM_FROM_DATA("message_caller") break; case CompCallerId: // 5.4.11 SET_PARAM_FROM_DATA("caller_networkprovided") break; case FirstCalledId: // 5.4.13 SET_PARAM_FROM_DATA("ffwd_first") break; case MWICount: // 5.4.14 CHECK_LEN(1) tmp = (int)(*pdata); params.addParam("message_count",tmp); break; case FwdCallType: // 5.4.15 CHECK_LEN(1) SET_PARAM_FROM_DICT("ffwd_reason",s_dict_ffwdReason) break; case RedirNumber: // 5.4.17 SET_PARAM_FROM_DATA("ffwd_last") break; case Charge: // 5.4.18 Debug(this,DebugStub,"Skipping %s parameter [%p]",pname,this); continue; case AdditionalCharge: // 5.4.19 Debug(this,DebugStub,"Skipping %s parameter [%p]",pname,this); continue; case Duration: // 5.4.20 CHECK_LEN(6) setDateTime(tmp,(char*)pdata,6); params.addParam("duration",tmp); break; case NetworkID: // 5.4.21 SET_PARAM_FROM_DATA("netid") break; case CarrierId: // 5.4.22 SET_PARAM_FROM_DATA("carrierid") break; case SelectFunction: // 5.4.23 Debug(this,DebugStub,"Skipping %s parameter [%p]",pname,this); continue; case Display: // 5.4.24 Debug(this,DebugStub,"Skipping %s parameter [%p]",pname,this); continue; case ServiceInfo: // 5.4.25 CHECK_LEN(1) if (*pdata > 1) tmp = (int)(*pdata); else tmp = *pdata ? "active" : "not-active"; params.addParam("service_info",tmp); break; case Extension: // 5.4.26 Debug(this,DebugStub,"Skipping %s parameter [%p]",pname,this); continue; } #undef SET_PARAM_FROM_DATA #undef SET_PARAM_FROM_DICT #undef CHECK_LEN DDebug(this,DebugAll,"Decoded %s=%s [%p]",pname,tmp.c_str(),this); } if (recvParams(msg,params)) return true; return UART::error(EStopped); }
// Handle received digital data void T38Terminal::rxData(const DataBlock& data, unsigned long tStamp) { t38_core_rx_ifp_packet(t38_get_t38_state(&m_t38),(uint8_t*)data.data(),data.length(),tStamp & 0xffff); }