From 0b624384cd52be20e61284551d832b499d7b7707 Mon Sep 17 00:00:00 2001 From: Jonathan McCrohan Date: Sat, 14 Apr 2012 12:56:48 +0100 Subject: Imported Upstream version 2.1.8.20120216 --- cphidgetir.c | 1889 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1889 insertions(+) create mode 100644 cphidgetir.c (limited to 'cphidgetir.c') diff --git a/cphidgetir.c b/cphidgetir.c new file mode 100644 index 0000000..dc58dd1 --- /dev/null +++ b/cphidgetir.c @@ -0,0 +1,1889 @@ +#include "stdafx.h" +#include "cphidgetir.h" +#include "cusb.h" +#include "csocket.h" +#include "cthread.h" +#include "utils/utils.h" + +// === Internal Functions === // +static int analyze_data(CPhidgetIRHandle phid, int trailgap_needed); +static int learn_data(CPhidgetIRHandle phid); +#define ABS(x) ((x) < 0 ? -(x) : (x)) +#define pdiff(a, b) ( ABS((a) - (b)) / (double)( ((a) + (b)) / 2.0 ) ) + +//clearVars - sets all device variables to unknown state +CPHIDGETCLEARVARS(IR) + TESTPTR(phid); + + //set data to unknown + phid->polarity = PUNK_BOOL; + phid->lastCodeKnown = PFALSE; + phid->lastRepeat = PUNK_BOOL; + phid->lastLearnedCodeKnown = PFALSE; + phid->lastGap = PUNK_BOOL; + + ZEROMEM(&phid->lastCodeInfo, sizeof(phid->lastCodeInfo)); + ZEROMEM(&phid->lastLearnedCodeInfo, sizeof(phid->lastLearnedCodeInfo)); + + //reset data pointers + phid->dataReadPtr = 0; + phid->dataWritePtr = 0; + phid->userReadPtr = 0; + phid->learnReadPtr = 0; + + phid->rawDataSendWSCounter = PUNK_INT; + + return EPHIDGET_OK; +} + +//initAfterOpen - sets up the initial state of an object, reading in packets from the device if needed +// used during attach initialization - on every attach +CPHIDGETINIT(IR) + TESTPTR(phid); + + //Make sure no old writes are still pending + phid->outputPacketLen = 0; + + //set data to unknown + phid->polarity = PUNK_BOOL; + phid->lastCodeKnown = PFALSE; + phid->lastRepeat = PUNK_BOOL; + phid->lastLearnedCodeKnown = PFALSE; + phid->lastGap = PUNK_BOOL; + + ZEROMEM(&phid->lastCodeInfo, sizeof(phid->lastCodeInfo)); + ZEROMEM(&phid->lastLearnedCodeInfo, sizeof(phid->lastLearnedCodeInfo)); + + //reset data pointers + phid->dataReadPtr = 0; + phid->dataWritePtr = 0; + phid->userReadPtr = 0; + phid->learnReadPtr = 0; + + phid->delayCode = PTRUE; + + #ifdef _WINDOWS + GetSystemTime(&phid->lastDataTime); + #else + gettimeofday(&phid->lastDataTime,NULL); + #endif + + phid->rawDataSendWSCounter = 1; + ZEROMEM(&phid->rawDataSendWSKeys, sizeof(int)*100); + + return EPHIDGET_OK; +} + +//dataInput - parses device packets +CPHIDGETDATA(IR) + int i, dataLength, us; + int data[IR_MAX_DATA_PER_PACKET]; + + if (length<0) return EPHIDGET_INVALIDARG; + TESTPTR(phid); + TESTPTR(buffer); + + //Parse device packets - store data locally + switch(phid->phid.deviceIDSpec) + { + case PHIDID_IR: + if ((phid->phid.deviceVersion >= 100) && (phid->phid.deviceVersion < 200)) + { + //move IR data into local storage + dataLength = buffer[0]; + for(i = 1; i <= dataLength; i++) + { + us = (((buffer[i * 2 - 1] & 0x7F) << 8) + buffer[i * 2]) * 10; //us + + //this means it's over the length we can measure + if(us >= IR_MAX_DATA_us) + us = PUNK_INT; + + if(phid->polarity == PUNK_BOOL) + { + //first time, set it + phid->polarity = buffer[i * 2 - 1] & 0x80; + + //if the us is not PUNK_INT, we have to add a PUNK_INT + //otherwise we can't recognize the first code + if(us != PUNK_INT) + { + phid->dataBuffer[phid->dataWritePtr] = PUNK_INT; + + phid->dataWritePtr++; + phid->dataWritePtr &= IR_DATA_ARRAY_MASK; + } + } + else + { + //switch it each time + phid->polarity ^= 0x80; + } + + data[i-1] = us; + phid->dataBuffer[phid->dataWritePtr] = us; + + phid->dataWritePtr++; + phid->dataWritePtr &= IR_DATA_ARRAY_MASK; + + //if we run into data that hasn't been read... too bad, we overwrite it and adjust the read pointer + if(phid->dataWritePtr == phid->dataReadPtr) + { + phid->dataReadPtr++; + phid->dataReadPtr &= IR_DATA_ARRAY_MASK; + } + + //make sure it's right + if(phid->polarity != (buffer[i * 2 - 1] & 0x80)) + { + LOG(PHIDGET_LOG_ERROR, "IR data has gotten out of sync!"); + //invalidate the buffer + phid->dataReadPtr = phid->dataWritePtr; + phid->userReadPtr = phid->dataWritePtr; + phid->polarity ^= 0x80; + } + } + } + else + return EPHIDGET_UNEXPECTED; + break; + default: + return EPHIDGET_UNEXPECTED; + } + + if(dataLength > 0) + { + #ifdef _WINDOWS + GetSystemTime(&phid->lastDataTime); + #else + gettimeofday(&phid->lastDataTime,NULL); + #endif + phid->delayCode = PTRUE; + + //send out raw data event + FIRE(RawData, data, dataLength); + + //analyze data + analyze_data(phid, PTRUE); + learn_data(phid); + } + else + { + #ifdef _WINDOWS + TIME now; + FILETIME nowft, oldft, resft; + ULARGE_INTEGER nowul, oldul, resul; + double duration; + GetSystemTime(&now); + SystemTimeToFileTime(&now, &nowft); + SystemTimeToFileTime(&phid->lastDataTime, &oldft); + + nowul.HighPart = nowft.dwHighDateTime; + nowul.LowPart = nowft.dwLowDateTime; + oldul.HighPart = oldft.dwHighDateTime; + oldul.LowPart = oldft.dwLowDateTime; + + resul.HighPart = nowul.HighPart - oldul.HighPart; + resul.LowPart = nowul.LowPart - oldul.LowPart; + + resft.dwHighDateTime = resul.HighPart; + resft.dwLowDateTime = resul.LowPart; + + duration = (double)(resft.dwLowDateTime/10000000.0); + #else + struct timeval now; + gettimeofday(&now, NULL); + double duration = (now.tv_sec - phid->lastDataTime.tv_sec) + (double)((now.tv_usec-phid->lastDataTime.tv_usec)/1000000.0); + #endif + if(duration > IR_MAX_GAP_LENGTH/1000000 && phid->delayCode) + { + //didn't get any data, so try and look at what we do have + //run this if we haven't had data for > MAX_GAP + analyze_data(phid, PFALSE); + phid->delayCode = PFALSE; + } + if(duration > 0.3) //300 ms for learning + { + //didn't get any data, so try and look at what we do have + learn_data(phid); + } + } + + return EPHIDGET_OK; +} + +//eventsAfterOpen - sends out an event for all valid data, used during attach initialization +CPHIDGETINITEVENTS(IR) + TESTPTR(phid); + return EPHIDGET_OK; +} + +//getPacket - used by write thread to get the next packet to send to device +CGETPACKET_BUF(IR) + +//sendpacket - sends a packet to the device asynchronously, blocking if the 1-packet queue is full +CSENDPACKET_BUF(IR) + +static int sendRAWData(CPhidgetIRHandle phid, int *data, int length, int carrier, int dutyCycle, int gap) +{ + unsigned char buffer[10]; + int i, j, result; + + if(length > 0xff) + return EPHIDGET_INVALIDARG; + + CThread_mutex_lock(&phid->phid.writelock); + + buffer[0] = IR_DEFINEDATA_PACKET; + buffer[1] = (length >> 8) & 0xff; + buffer[2] = length & 0xff; + buffer[3] = (unsigned char)round(((double)(1 / (double)carrier) * 1600000.0 - 1)); //period + buffer[4] = (unsigned char)round((((double)(1 / (double)carrier) * 1600000.0 - 1) * ((double)dutyCycle/100.0))); //pulse width + buffer[5] = (unsigned char)(round(gap/10.0) >> 8) & 0xff; + buffer[6] = (unsigned char)round(gap/10.0) & 0xff; + //can leave this out to receive while we transmit - TESTING ONLY! + buffer[7] = IR_STOP_RX_WHILE_TX_FLAG; + + if ((result = CPhidgetIR_sendpacket(phid, buffer)) != EPHIDGET_OK) + { + CThread_mutex_unlock(&phid->phid.writelock); + return result; + } + + for (i = 0; i < length; i++) + { + j = i % 8; + buffer[j] = data[i]; + if (j == 7 || i == (length-1)) + { + if ((result = CPhidgetIR_sendpacket(phid, buffer)) != EPHIDGET_OK) + { + CThread_mutex_unlock(&phid->phid.writelock); + return result; + } + } + } + + CThread_mutex_unlock(&phid->phid.writelock); + + return EPHIDGET_OK; +} + +//Note that data is sent to board as 10's of us's +#define TIME_TO_PACKET(data, length, index, us) \ + do{ \ + if ((index + 2) >= length) \ + return EPHIDGET_NOMEMORY; \ + if ((us) > 327670) \ + return EPHIDGET_INVALIDARG; \ + if((us) > 1270) /*1270 == 0x7f << 1*/ \ + data[index++] = ((round((us)/10) >> 8) | 0x80); \ + data[index++] = round((us)/10) & 0xff; \ + *time += (us); \ + } while(0) + +//returned data must start and end with pulses +static int RawTimeDataToRawData(int *rawTimeData, int rawTimeDataLength, int *rawData, int *rawDataLength, int *time) +{ + int i=0, j; + + for(j=0;j= *rawDataLength) + return EPHIDGET_NOMEMORY; + + TIME_TO_PACKET(rawData, *rawDataLength, i, codeInfo.repeat[j]); + j++; + } + } + //actually do some work + else + { + i = 0; + if(codeInfo.header[0]) + { + TIME_TO_PACKET(rawData, *rawDataLength, i, codeInfo.header[0]); + TIME_TO_PACKET(rawData, *rawDataLength, i, codeInfo.header[1]); + } + + switch(codeInfo.encoding) + { + case PHIDGET_IR_ENCODING_SPACE: + case PHIDGET_IR_ENCODING_PULSE: + //k needs to be ie 1 for 15 bit codes, 0 for 16 bit codes, 7 for 9 bit codes... + //because code[0] is MSB and may have less then 8 bit, where all other elements do have 8 bits + for(j = codeInfo.bitCount-1, k = (8 - (codeInfo.bitCount % 8) ) % 8; j >= 0; j--, k++) + { + if(code[k/8] & (1 << (j % 8))) + { + TIME_TO_PACKET(rawData, *rawDataLength, i, codeInfo.one[0]); + TIME_TO_PACKET(rawData, *rawDataLength, i, codeInfo.one[1]); + } + else + { + TIME_TO_PACKET(rawData, *rawDataLength, i, codeInfo.zero[0]); + TIME_TO_PACKET(rawData, *rawDataLength, i, codeInfo.zero[1]); + } + } + + //add trail pulse for space encoding, delete last space for pulse encoding + if(codeInfo.encoding == PHIDGET_IR_ENCODING_SPACE) + { + TIME_TO_PACKET(rawData, *rawDataLength, i, codeInfo.trail); + } + else + { + if(code[0] & 0x01) + { + if(codeInfo.one[1] > 1270) + i--; + i--; + *time -= codeInfo.one[1]; + } + else + { + if(codeInfo.zero[1] > 1270) + i--; + i--; + *time -= codeInfo.zero[1]; + } + } + + break; + case PHIDGET_IR_ENCODING_BIPHASE: + case PHIDGET_IR_ENCODING_RC5: + case PHIDGET_IR_ENCODING_RC6: + //this uses space-pulse for 0 and pulse-space for 1, and has the long bit-6 (trailer bit) + + //1st bit is a special case + j = codeInfo.bitCount-1; + k = (8 - (codeInfo.bitCount % 8) ) % 8; + if(code[k/8] & (1 << (j % 8))) + { + if(codeInfo.encoding == PHIDGET_IR_ENCODING_RC6) + TIME_TO_PACKET(rawData, *rawDataLength, i, codeInfo.one[0]); + lastbit = 1; + } + else + { + if(codeInfo.encoding != PHIDGET_IR_ENCODING_RC6) + TIME_TO_PACKET(rawData, *rawDataLength, i, codeInfo.zero[0]); + lastbit = 0; + } + + //now do the rest of the bits - each time trough, we do the last hald of the last bit and the first half of this bit + for(j--, k++; j >= 0; j--, k++) + { + int bit = codeInfo.bitCount - j - 1; + //1 + if(code[k/8] & (1 << (j % 8))) + { + if(lastbit) + { + if(codeInfo.encoding == PHIDGET_IR_ENCODING_RC6 && bit == 5) + TIME_TO_PACKET(rawData, *rawDataLength, i, codeInfo.one[1] * 2); + else + TIME_TO_PACKET(rawData, *rawDataLength, i, codeInfo.one[1]); + + if(codeInfo.encoding == PHIDGET_IR_ENCODING_RC6 && bit == 4) + TIME_TO_PACKET(rawData, *rawDataLength, i, codeInfo.one[0] * 2); + else + TIME_TO_PACKET(rawData, *rawDataLength, i, codeInfo.one[0]); + } + else + { + if(codeInfo.encoding == PHIDGET_IR_ENCODING_RC6 && bit == 4) + TIME_TO_PACKET(rawData, *rawDataLength, i, codeInfo.zero[1] + codeInfo.one[0] * 2); + else if(codeInfo.encoding == PHIDGET_IR_ENCODING_RC6 && bit == 5) + TIME_TO_PACKET(rawData, *rawDataLength, i, codeInfo.zero[1] * 2 + codeInfo.one[0]); + else + TIME_TO_PACKET(rawData, *rawDataLength, i, codeInfo.zero[1] + codeInfo.one[0]); + } + + lastbit = 1; + } + //0 + else + { + if(lastbit) + { + if(codeInfo.encoding == PHIDGET_IR_ENCODING_RC6 && bit == 4) + TIME_TO_PACKET(rawData, *rawDataLength, i, codeInfo.one[1] + codeInfo.zero[0] * 2); + else if(codeInfo.encoding == PHIDGET_IR_ENCODING_RC6 && bit == 5) + TIME_TO_PACKET(rawData, *rawDataLength, i, codeInfo.one[1] * 2 + codeInfo.zero[0]); + else + TIME_TO_PACKET(rawData, *rawDataLength, i, codeInfo.one[1] + codeInfo.zero[0]); + } + else + { + if(codeInfo.encoding == PHIDGET_IR_ENCODING_RC6 && bit == 5) + TIME_TO_PACKET(rawData, *rawDataLength, i, codeInfo.zero[1] * 2); + else + TIME_TO_PACKET(rawData, *rawDataLength, i, codeInfo.zero[1]); + + if(codeInfo.encoding == PHIDGET_IR_ENCODING_RC6 && bit == 4) + TIME_TO_PACKET(rawData, *rawDataLength, i, codeInfo.zero[0] * 2); + else + TIME_TO_PACKET(rawData, *rawDataLength, i, codeInfo.zero[0]); + } + + lastbit = 0; + } + } + + //finish up the data stream if we need to add a last pulse + if(lastbit && codeInfo.encoding != PHIDGET_IR_ENCODING_RC6) + TIME_TO_PACKET(rawData, *rawDataLength, i, codeInfo.one[1]); + if(!lastbit && codeInfo.encoding == PHIDGET_IR_ENCODING_RC6) + TIME_TO_PACKET(rawData, *rawDataLength, i, codeInfo.zero[1]); + + break; + //case PHIDGET_IR_ENCODING_RAW: + case PHIDGET_IR_ENCODING_UNKNOWN: + default: + return EPHIDGET_INVALIDARG; + } + } + + *rawDataLength = i; + + return EPHIDGET_OK; +} + +static int CCONV_CDECL compare_int(const void *arg1, const void *arg2 ) +{ + int *int1 = (int *)arg1; + int *int2 = (int *)arg2; + return *int1 - *int2; +} + +//figure out the different times and their average values +//we'll need to use max error (30%?) +//we're going to keep track of average diff, and when we find a diff > 5xAvg, or > 20% switch to next section +//must have at least a 10% difference between lengths +//also if we find too many different times, error +static int get_times(int *timesin, int incount, int *timesout, int *timesoutcount, int *outcount) +{ + int edge = 0, i = 0, j = 0, k = 0; + double avgDiff = 0, diffSum = 0; + for (i = 1; i < incount; i++) + { + double diff = pdiff(timesin[i], timesin[i-1]); + + if (((avgDiff > 0) && (diff > (avgDiff * 10.0)) && diff > 0.1) || diff > 0.2) + { + //we've found a boundary + //find average of all lower values + double avg = 0; + for (j = edge; j < i; j++) + avg += timesin[j]; + avg /= (double)(i - edge); + timesout[k] = round(avg); + timesoutcount[k++] = (i - edge); + + if((k+1) > *outcount) + return EPHIDGET_OUTOFBOUNDS; + + edge = i; + diffSum = 0; + avgDiff = 0; + } + else + { + diffSum += diff; + avgDiff = diffSum / (i - edge); + } + } + //get the last section... + avgDiff = 0; + for (j = edge; j < incount; j++) + avgDiff += timesin[j]; + avgDiff /= (double)(incount - edge); + timesoutcount[k] = (incount - edge); + timesout[k++] = round(avgDiff); + + *outcount = k; + return EPHIDGET_OK; +} + +#define COUNT_TIMES(highlow, index) \ + if(highlow##s[index] == data[i] || !highlow##s[index]) \ + { \ + highlow##s[index] = data[i]; \ + highlow##Counts[index]++; \ + if(highlow##count < index+1) \ + highlow##count=index+1; \ + continue; \ + } + +#define ORDER_TIMES(highlow, index) \ + if(highlow##s[index+1] && (highlow##s[index+1] < highlow##s[index])) \ + { \ + int temp = highlow##Counts[index]; \ + highlow##Counts[index] = highlow##Counts[index+1]; \ + highlow##Counts[index+1] = temp; \ + temp = highlow##s[index]; \ + highlow##s[index] = highlow##s[index+1]; \ + highlow##s[index+1] = temp; \ + } + +//we can count on data having at most two high and two low lengths (except for RC-6) +//the first entry in data is a pulse, and the last is a pulse +//outlength will be in bits, but it should be specified in bytes (array size) +static int decode_data(int *data, int inlength, unsigned char *decoded_data, int *outlength, CPhidgetIR_CodeInfoHandle codeInfo) +{ + int i, highcount = 0, lowcount = 0, bitcount = 0, j = 0, n = 0; + //room for 4 + int highs[5] = {0, 0, 0, 0, 0}, highCounts[5] = {0, 0, 0, 0, 0}, lows[5] = {0, 0, 0, 0, 0}, lowCounts[5] = {0, 0, 0, 0, 0}; + unsigned char decode_temp[IR_MAX_CODE_DATA_LENGTH]; + + + ZEROMEM(decode_temp, IR_MAX_CODE_DATA_LENGTH); + //get the numbers - only allow up to four each of high/low!! + for(i = 0; i < inlength; i++) + { + if(!(i%2)) + { + COUNT_TIMES(high, 0) + COUNT_TIMES(high, 1) + COUNT_TIMES(high, 2) + COUNT_TIMES(high, 3) + + goto errorexit; + } + else + { + COUNT_TIMES(low, 0) + COUNT_TIMES(low, 1) + COUNT_TIMES(low, 2) + COUNT_TIMES(low, 3) + + goto errorexit; + } + + //LOG(PHIDGET_LOG_DEBUG, "%07d,",data[i]); + } + + //this sorts them + for(i = 0; i < 5; i++) + { + ORDER_TIMES(high, 0) + ORDER_TIMES(high, 1) + ORDER_TIMES(high, 2) + ORDER_TIMES(high, 3) + ORDER_TIMES(low, 0) + ORDER_TIMES(low, 1) + ORDER_TIMES(low, 2) + ORDER_TIMES(low, 3) + } + //sanity checks + if(lowcount == 0 || highcount == 0) + goto errorexit; + + //figure out the encoding + //Bi-phase + //either one high or two highs and one is twice the other (10% error) + //and either one low or two lows and one is twice the other (10% error) + //make sure that lows and highs are same length (30% error) + if + ( + ( + (highcount == 1) && + (lowcount == 1) && + (pdiff(highs[0], lows[0]) < 0.3) + ) || + ( + (highcount == 1) && + (lowcount >= 2) && + (pdiff(highs[0], lows[0]) < 0.3) && + (pdiff(lows[1] / 2.0, lows[0]) < 0.1) + ) || + ( + (highcount >= 2) && + (lowcount == 1) && + (pdiff(highs[0], lows[0]) < 0.3) && + (pdiff(highs[1] / 2.0, highs[0]) < 0.1) + ) || + ( + (highcount >= 2) && + (lowcount >= 2) && + (pdiff(highs[0], lows[0]) < 0.3) && + (pdiff(highs[1], lows[1]) < 0.3) && + (pdiff(highs[1] / 2.0, highs[0]) < 0.1) && + (pdiff(lows[1] / 2.0, lows[0]) < 0.1) + ) + ) + { + //could also be RC-5 or RC-6 + codeInfo->encoding = PHIDGET_IR_ENCODING_BIPHASE; + goto biphase; + } + //check for some special RC-6 cases + //there will always be either 2 or 3 of high/low + //there will always be a short high pulse + else if ( + ( + //1x, 2x, 3x high, 2x low + (highcount == 3) && + (lowcount == 1) && + (pdiff(highs[1], lows[0]) < 0.3) && + (pdiff(highs[2] / 3.0, highs[0]) < 0.1) + ) || + ( + //1x + 3x high, 1x, 2x, 3x low + (highcount == 2) && + (lowcount == 3) && + (pdiff(highs[0], lows[0]) < 0.3) && + (pdiff(highs[1], lows[2]) < 0.3) && + (pdiff(lows[1] / 2.0, lows[0]) < 0.1) && + (pdiff(lows[2] / 3.0, lows[0]) < 0.1) + ) || + ( + //1x, 2x, 3x high, 2x, 3x low + (highcount == 3) && + (lowcount == 2) && + (pdiff(highs[2], lows[1]) < 0.3) && + (pdiff(highs[1], lows[0]) < 0.3) && + (pdiff(highs[1] / 2.0, highs[0]) < 0.1) && + (pdiff(highs[2] / 2.0, highs[1]) < 0.1) + ) + ) + { + //RC-6 mode 0 always starts with bit0 == 1 (444us pulse - 444us space) + //header is 2.666us pulse - 889us space + //bit4 == toggle bit, and had double bit time (889us - 889us) + //full length is 21 bits + //trailing gap (signal free time) is 2.666us + codeInfo->encoding = PHIDGET_IR_ENCODING_RC6; + goto biphase; + } + else + { + goto notbiphase; + } + +biphase: + { + int bit, startspace = 0, halfbit = 0, rc6temp = 0; + + //find pulse short pulse width - do an average if possible + int pulse = lows[0]; + if(pdiff(lows[0], highs[0]) < 0.3) + { + pulse = round((lows[0]+highs[0]) / 2.0); + } + else if(highs[0] < pulse) + pulse = highs[0]; + + + codeInfo->one[0] = pulse; + codeInfo->one[1] = pulse; + codeInfo->zero[0] = pulse; + codeInfo->zero[1] = pulse; + + //make sure 1st pulse is short + if(pdiff(data[0], pulse) > 0.3) + goto notbiphase; + + //it's biphase! now decode.. + //note that there may be an 'invisible' starting space + //- we can see this if the first long pulse is on an even i + //note this could cause trouble for RC-6 when there isn't a 2x pulse before the TR bit, + // or even for normal biphase/RC-5 when there isn't a 2x at all + //default is 1 - this should be good for all but RC-6 + startspace = 1; + for(i=0; iencoding = PHIDGET_IR_ENCODING_RC6; + } + //found a double pulse + else if(pdiff(data[i] / 2.0, pulse) < 0.2) + { + //probably this is PDM or PWM, but it could also be RC-6 + if(!halfbit) + { + //check for RC-6 + if(bitcount==4) + { + halfbit ^= 1; + rc6temp = 1; + codeInfo->encoding = PHIDGET_IR_ENCODING_RC6; + } + else + goto notbiphase; + } + else + { + if(rc6temp == 1) + { + rc6temp = 0; + halfbit ^= 1; + } + else + { + bit ^= 1; + } + bitcount++; + } + } + else + { + if(halfbit) + bitcount++; + halfbit ^= 1; + } + } + decode_temp[bitcount/8] |= bit << (bitcount % 8); + if((halfbit && !bit) || (!startspace && bit)) + bitcount++; + + //success, presumably + //see if it's RC-5 - 889 pulse time + //TODO also check data length, etc. + if(pdiff(pulse, 889) < 0.2 + && codeInfo->encoding != PHIDGET_IR_ENCODING_RC6 + && startspace) + { + codeInfo->encoding = PHIDGET_IR_ENCODING_RC5; + } + + //TODO: if RC-6, make sure data length, bit times, etc. match, otherwise, it's not biphase! + + goto done; + } + +notbiphase: + //make sure it's zeroed - we may have started filling it in in the biphase section + ZEROMEM(decode_temp, IR_MAX_CODE_DATA_LENGTH); + //Pulse Distance Modulation (PDM) - most common (NEC, etc.) + //should be a trailing pulse that we just ignore for now - it just lets us get the trailing space width + if(highcount == 1 && lowcount == 2) + { + int dataTimeZero = lows[1], dataTimeOne = lows[0]; + if(lows[1] > lows[0]) + { + dataTimeZero = lows[0]; + dataTimeOne = lows[1]; + } + //get the data + bitcount = 0; + for (i = 0; i < (inlength-1); i+=2) + { + if (data[i + 1] == dataTimeOne) + decode_temp[bitcount/8] |= (1 << (bitcount % 8)); + bitcount++; + } + + codeInfo->one[0] = highs[0]; + codeInfo->one[1] = dataTimeOne; + codeInfo->zero[0] = highs[0]; + codeInfo->zero[1] = dataTimeZero; + + codeInfo->trail = data[inlength-1]; + codeInfo->encoding = PHIDGET_IR_ENCODING_SPACE; + } + //PWM (SIRC) + //in PWM, we take the trailing pulse to be a bit + else if(highcount == 2 && lowcount == 1) + { + int dataTimeZero = highs[1], dataTimeOne = highs[0]; + if(highs[1] > highs[0]) + { + dataTimeZero = highs[0]; + dataTimeOne = highs[1]; + } + //get the data + bitcount = 0; + for (i = 0; i < inlength; i+=2) + { + if (data[i] == dataTimeOne) + decode_temp[bitcount/8] |= (1 << (bitcount % 8)); + bitcount++; + } + + codeInfo->one[0] = dataTimeOne; + codeInfo->one[1] = lows[0]; + codeInfo->zero[0] = dataTimeZero; + codeInfo->zero[1] = lows[0]; + + codeInfo->encoding = PHIDGET_IR_ENCODING_PULSE; + } + else + goto errorexit; + +done: + //check size of output buffer + if(*outlength < ((bitcount / 8) + ((bitcount % 8) ? 1 : 0))) + goto errorexit; + + //limit - code have to be >= 4-bit + if(bitcount < 4) + goto errorexit; + + codeInfo->bitCount = bitcount; + *outlength = (bitcount / 8) + ((bitcount % 8) ? 1 : 0); + + //data is LSB first now, lets flip it to MSB first - we don't change the Byte order though, just the bit order in the bytes + n = bitcount; + + ZEROMEM(decoded_data, *outlength); + for(i = 0, j = bitcount-1, n = (*outlength * 8) - 1; i < bitcount; i++, j--, n--) + decoded_data[n/8] |= ( ( decode_temp[j/8] & (1 << (j%8)) ) ? (0x01 << (i%8)) : 0); + + return EPHIDGET_OK; + +errorexit: + codeInfo->encoding = PHIDGET_IR_ENCODING_UNKNOWN; + return EPHIDGET_UNEXPECTED; +} + +//uses the data array +static int dataTime(int *data, int *time, int ptr, int endptr) +{ + int i; + int length = endptr - ptr; + if(length < 0) + length += IR_DATA_ARRAY_SIZE; + *time = 0; + for(i = 0; i < length; i++) + { + *time += data[ptr]; + ptr=(ptr+1)&IR_DATA_ARRAY_MASK; + } + return EPHIDGET_OK; +} + +//uses the data array +static int compareDataArrays(int *data, int ptr1, int ptr2, int endptr1, int endptr2, double maxpdiff) +{ + int i; + + int length1 = endptr1 - ptr1; + int length2 = endptr2 - ptr2; + + if(length1 < 0) + length1 += IR_DATA_ARRAY_SIZE; + if(length2 < 0) + length2 += IR_DATA_ARRAY_SIZE; + + if(length1 != length2) + return PFALSE; + + for(i = 0; i < length1; i++) + { + if(pdiff(data[ptr1], data[ptr2]) > maxpdiff) + return PFALSE; + + ptr1=(ptr1+1)&IR_DATA_ARRAY_MASK; + ptr2=(ptr2+1)&IR_DATA_ARRAY_MASK; + } + return PTRUE; +} + +//this tries to lean a code +//we read staring at a long gap (PUNK_INT) until the next long gap, or time > 1 second +//need at least 1 code and 3 repeats to figure out the code completely +static int learn_data(CPhidgetIRHandle phid) +{ + int dataReader, dataWriter, highcount, lowcount, high_low, i, decodedcount = IR_MAX_CODE_DATA_LENGTH; + int highs[IR_DATA_ARRAY_SIZE/2], lows[IR_DATA_ARRAY_SIZE/2], code_data[IR_DATA_ARRAY_SIZE]; + unsigned char decoded_data[IR_MAX_CODE_DATA_LENGTH]; + int highFinalscount=20, lowFinalscount=20; + int highFinals[20], lowFinals[20]; + int highFinalsCounts[20], lowFinalsCounts[20]; + //CPhidgetIR_Encoding encoding; + unsigned char hasHeader = PFALSE; + + CPhidgetIR_CodeInfo codeInfo; + + //keeps track of packets in the data stream + int dataPackets[50]; + int dataPacketsCounter = 0; + + int timecounter = 0; + + //int oldLearPtr = phid->learnReadPtr; + int readToPtr; + + //setup codeInfo with defaults + ZEROMEM(&codeInfo, sizeof(codeInfo)); + codeInfo.carrierFrequency = 38000; + codeInfo.encoding = PHIDGET_IR_ENCODING_UNKNOWN; + codeInfo.length = PHIDGET_IR_LENGTH_UNKNOWN; + //codeInfo.maxPdiff = 0.30; + codeInfo.min_repeat = 1; + codeInfo.dutyCycle = 50; + + //we have to have a BIG gap to start + while(phid->dataBuffer[phid->learnReadPtr] < PUNK_INT) + { + //nothing to analyze yet + if(phid->learnReadPtr == phid->dataWritePtr) + return EPHIDGET_OK; + + phid->learnReadPtr++; + phid->learnReadPtr &= IR_DATA_ARRAY_MASK; + } + + //nothing to analyze yet + if(phid->learnReadPtr == phid->dataWritePtr) + return EPHIDGET_OK; + + dataReader = ((phid->learnReadPtr+1) & IR_DATA_ARRAY_MASK); + + //nothing to analyze yet + if(dataReader == phid->dataWritePtr) + return EPHIDGET_OK; + + readToPtr = dataReader; + //when read pointer != write pointer, there is new data to read + //read pointer should point at first spot that's probably a gap + while(phid->dataBuffer[readToPtr] != PUNK_INT) + { + //back up to last gap if we run into write pointer, or have > 1 sec. of data + if(readToPtr == phid->dataWritePtr || timecounter >= 2000000) + { + while(phid->dataBuffer[readToPtr] < IR_MIN_GAP_LENGTH) + { + //nothing to analyze yet + if(readToPtr == dataReader) + return EPHIDGET_OK; + + readToPtr--; + readToPtr &= IR_DATA_ARRAY_MASK; + } + goto analyze_step_one; + } + + timecounter += phid->dataBuffer[readToPtr]; + + readToPtr++; + readToPtr &= IR_DATA_ARRAY_MASK; + } + + //we should have at least enough data for one set plus its gap +analyze_step_one: + + //this grabs everything, including the gaps + highcount = 0; + lowcount = 0; + high_low = 1; + while(dataReader != readToPtr) + { + //add data to high/low arrays + //we start with a high pulse + if(high_low == 1) + highs[highcount++] = phid->dataBuffer[dataReader]; + else + lows[lowcount++] = phid->dataBuffer[dataReader]; + + high_low ^= 1; + dataReader = ((dataReader + 1) & IR_DATA_ARRAY_MASK); + } + + //nothing to analyze yet + if(highcount == 0 && lowcount == 0) + goto advance_exit; + + //not enough data + if((highcount+lowcount) < 10) + goto advance_exit; + + //sort the high/low arrays and extract their components + qsort(highs, highcount, sizeof(int), compare_int); + qsort(lows, lowcount, sizeof(int), compare_int); + + get_times(highs, highcount, highFinals, highFinalsCounts, &highFinalscount); + get_times(lows, lowcount, lowFinals, lowFinalsCounts, &lowFinalscount); + + + //go back through the data buffer and fill in dataBufferNormalized, doesn't include any gap data + dataWriter = ((phid->learnReadPtr+1) & IR_DATA_ARRAY_MASK); + high_low = 1; + dataPackets[dataPacketsCounter++] = dataWriter; + while(dataWriter != dataReader) + { + //high time + if (high_low) + { + int newtime = highFinals[0]; + double matchPercent = 1; + for(i = 0; idataBuffer[dataWriter], highFinals[i]); + if (diff <= 0.30 && diff < matchPercent) + { + newtime = highFinals[i]; + matchPercent = diff; + } + } + phid->dataBufferNormalized[dataWriter] = newtime; + } + //low time + else + { + int newtime = lowFinals[0]; + double matchPercent = 1; + for(i = 0; idataBuffer[dataWriter], lowFinals[i]); + if (diff <= 0.30 && diff < matchPercent) + { + newtime = lowFinals[i]; + matchPercent = diff; + } + } + phid->dataBufferNormalized[dataWriter] = newtime; + + if(newtime >= IR_MIN_GAP_LENGTH) + dataPackets[dataPacketsCounter++] = ((dataWriter + 1) & IR_DATA_ARRAY_MASK); + } + + high_low ^= 1; + dataWriter = ((dataWriter + 1) & IR_DATA_ARRAY_MASK); + } + + //at this point we have the data normalized, need to break off the first packet, figure out the gap, repeat, etc. + //first make sure we have a data packet and at least 3 repeat packets. + + //not enough data + if(dataPacketsCounter < 5) + goto advance_exit; + + //strip the header - if 1st pulse and/or the 1st space is unique, we assume first pulse/space is a header + //won't be unique because we have the repeats included now... + dataReader = dataPackets[0]; + for(i=0;idataBufferNormalized[dataReader] == highFinals[i] && (highFinalsCounts[i] == 1 || highFinalsCounts[i] == dataPacketsCounter)) + { + dataReader = (dataReader + 2) & IR_DATA_ARRAY_MASK; + hasHeader = PTRUE; + codeInfo.header[0] = phid->dataBufferNormalized[dataPackets[0]]; + codeInfo.header[1] = phid->dataBufferNormalized[(dataPackets[0] + 1) & IR_DATA_ARRAY_MASK]; + } + + //put the code data into the code array + i=0; + //include the trailing bit! + //dataWriter = ((dataWriter - 1) & IR_DATA_ARRAY_MASK); + while(dataPackets[1] != dataReader) + { + code_data[i++] = phid->dataBufferNormalized[dataReader]; + dataReader = ((dataReader + 1) & IR_DATA_ARRAY_MASK); + } + + //no gap for decoding + i--; + //try to decode + if(decode_data(code_data, i, decoded_data, &decodedcount, &codeInfo)) + { + //couldn't find the encoding + goto exit; + } + + //get gap time + { + int time1 = 0, time2 = 0; + codeInfo.length = PHIDGET_IR_LENGTH_CONSTANT; + dataTime(phid->dataBufferNormalized, &time1, dataPackets[0], dataPackets[1]); + for(i=1; i<(dataPacketsCounter-1); i++) + { + dataTime(phid->dataBufferNormalized, &time2, dataPackets[i], dataPackets[i+1]); + if(pdiff(time1, time2) > 0.3) + codeInfo.length = PHIDGET_IR_LENGTH_VARIABLE; + } + if(codeInfo.length == PHIDGET_IR_LENGTH_CONSTANT) + { + codeInfo.gap = time1; + } + else + { + int constgap = PTRUE; + for(i=1; i<(dataPacketsCounter-1); i++) + { + if(phid->dataBufferNormalized[dataPackets[i]-1] != phid->dataBufferNormalized[dataPackets[i+1]-1]) + constgap = PFALSE; + } + if(constgap) + codeInfo.gap = phid->dataBufferNormalized[dataPackets[1]-1]; + else + { + //not constant length and no constant gap - no sense! + LOG(PHIDGET_LOG_DEBUG, "Couldn't figure out the gap"); + goto exit; + } + } + } + + //figure out repeat/toggle + //first two don't match, so there may be some sort of repeat code or toggling + if(compareDataArrays(phid->dataBufferNormalized, dataPackets[0], dataPackets[1], dataPackets[1], dataPackets[2], 0.3) != PTRUE) + { + int j; + //packet 1 and 2 match - looks like a repeat code + if(compareDataArrays(phid->dataBufferNormalized, dataPackets[1], dataPackets[2], dataPackets[2], dataPackets[3], 0.3) == PTRUE) + { + //See if the 'repeat code' is just the same code without the header (don't compare gaps) + if(codeInfo.header[0] && compareDataArrays( + phid->dataBufferNormalized, (dataPackets[0]+2) & IR_DATA_ARRAY_MASK, dataPackets[1], + (dataPackets[1]-1) & IR_DATA_ARRAY_MASK, (dataPackets[2]-1) & IR_DATA_ARRAY_MASK, 0.3) + ) + { + LOG(PHIDGET_LOG_DEBUG, "No repeat code, just repeating without the header."); + codeInfo.repeat[0] = 0; + } + else + { + for(i = dataPackets[1], j = 0; i != dataPackets[2]; i = (i+1) & IR_DATA_ARRAY_MASK, j++) + { + //Make sure we don't go over the length of the repeat array! + if(j >= IR_MAX_REPEAT_LENGTH) + { + LOG(PHIDGET_LOG_DEBUG, "Couldn't figure out the repeat code"); + goto exit; + } + codeInfo.repeat[j] = phid->dataBufferNormalized[i]; + } + //don't include the gap + codeInfo.repeat[j-1] = 0; + } + } + //packets 0 and 2 match and 1 and 3 match - looks like a two packet data sequence, with some toggleing. + else if(compareDataArrays(phid->dataBufferNormalized, dataPackets[0], dataPackets[2], dataPackets[1], dataPackets[3], 0.3) == PTRUE + && compareDataArrays(phid->dataBufferNormalized, dataPackets[1], dataPackets[3], dataPackets[2], dataPackets[4], 0.3) == PTRUE) + { + int decodedcount2 = IR_MAX_CODE_DATA_LENGTH; + int code_data2[IR_DATA_ARRAY_SIZE]; + unsigned char decoded_data2[IR_MAX_CODE_DATA_LENGTH]; + CPhidgetIR_CodeInfo codeInfo2; + ZEROMEM(&codeInfo2, sizeof(codeInfo2)); + + dataReader = dataPackets[1]; + //header + if(codeInfo.header[0]) + dataReader = (dataReader + 2) & IR_DATA_ARRAY_MASK; + //put the code data into the code array + i=0; + //include the trailing bit! + //dataWriter = ((dataWriter - 1) & IR_DATA_ARRAY_MASK); + while(dataPackets[2] != dataReader) + { + code_data2[i++] = phid->dataBufferNormalized[dataReader]; + dataReader = ((dataReader + 1) & IR_DATA_ARRAY_MASK); + } + + //no gap for decoding + i--; + //try to decode + if(decode_data(code_data2, i, decoded_data2, &decodedcount2, &codeInfo2)) + { + goto exit; + } + + if(codeInfo.encoding == codeInfo2.encoding && + codeInfo.bitCount == codeInfo2.bitCount && + decodedcount == decodedcount2) + { + for(i=0;ilastLearnedCode, sizeof(phid->lastLearnedCode)); + memcpy(phid->lastLearnedCode, decoded_data, decodedcount); + phid->lastLearnedCodeInfo = codeInfo; + phid->lastLearnedCodeKnown = PTRUE; + +exit: + + //adjust the learn read pointer + phid->learnReadPtr = readToPtr; + + //Done + return EPHIDGET_OK; + +advance_exit: + //not enough data, and readToPtr == PUNK_INT, so we can't expect any more data, and increment learnPtr + if(phid->dataBuffer[readToPtr] == PUNK_INT) + phid->learnReadPtr = readToPtr; + + return EPHIDGET_OK; +} + +//for now this just tries to get a data code +static int analyze_data(CPhidgetIRHandle phid, int trailgap_needed) +{ + int dataReader, dataWriter, highcount, lowcount, high_low, i, decodedcount = IR_MAX_CODE_DATA_LENGTH; + int highs[IR_DATA_ARRAY_SIZE/2], lows[IR_DATA_ARRAY_SIZE/2], code_data[IR_DATA_ARRAY_SIZE]; + unsigned char decoded_data[IR_MAX_CODE_DATA_LENGTH]; + int highFinalscount=20, lowFinalscount=20; + int highFinals[20], lowFinals[20]; + int highFinalsCounts[20], lowFinalsCounts[20]; + CPhidgetIR_CodeInfo codeInfo; + ZEROMEM(&codeInfo, sizeof(codeInfo)); + + //when read pointer != write pointer, there is new data to read + //read pointer should point at first spot that's probably a gap + while(phid->dataBuffer[phid->dataReadPtr] < IR_MIN_GAP_LENGTH) + { + //nothing to analyze yet + if(phid->dataReadPtr == phid->dataWritePtr) + return EPHIDGET_OK; + + phid->dataReadPtr++; + phid->dataReadPtr &= IR_DATA_ARRAY_MASK; + } + + //nothing to analyze yet + if(phid->dataReadPtr == phid->dataWritePtr) + return EPHIDGET_OK; + + dataReader = ((phid->dataReadPtr+1) & IR_DATA_ARRAY_MASK); + + //nothing to analyze yet + if(dataReader == phid->dataWritePtr) + return EPHIDGET_OK; + +//analyze_step_one: + //gets the data up to the first probable gap + highcount = 0; + lowcount = 0; + high_low = 1; + while(dataReader != phid->dataWritePtr) + { + //go till we find the next likely gap (long low pulse) + if((phid->dataBuffer[dataReader] >= IR_MIN_GAP_LENGTH) && (high_low == 0)) + goto analyze_step_two; + + //add data to high/low arrays + //we start with a high pulse + if(high_low == 1) + highs[highcount++] = phid->dataBuffer[dataReader]; + else + lows[lowcount++] = phid->dataBuffer[dataReader]; + + high_low ^= 1; + dataReader = ((dataReader + 1) & IR_DATA_ARRAY_MASK); + } + + //hit the write pointer before finding a gap + if(trailgap_needed) + return EPHIDGET_OK; + +analyze_step_two: + + //nothing to analyze yet + if(highcount == 0 && lowcount == 0) + return EPHIDGET_OK; + + phid->lastGap = phid->dataBuffer[phid->dataReadPtr]; + //should be one more high then low + if((highcount - 1) != lowcount) + { + LOG(PHIDGET_LOG_INFO, "Unexpected number of high/low pulses between gaps"); + return EPHIDGET_UNEXPECTED; + } + + //sort the high/low arrays and extract their components + qsort(highs, highcount, sizeof(int), compare_int); + qsort(lows, lowcount, sizeof(int), compare_int); + + get_times(highs, highcount, highFinals, highFinalsCounts, &highFinalscount); + get_times(lows, lowcount, lowFinals, lowFinalsCounts, &lowFinalscount); + + //go back through the data buffer and fills in dataBufferNormalized, doesn't include any gap data + dataWriter = ((phid->dataReadPtr+1) & IR_DATA_ARRAY_MASK); + high_low = 1; + while(dataWriter != dataReader) + { + //high time + if (high_low) + { + int newtime = highFinals[0]; + double matchPercent = 1; + for(i = 0; idataBuffer[dataWriter], highFinals[i]); + if (diff <= 0.30 && diff < matchPercent) + { + newtime = highFinals[i]; + matchPercent = diff; + } + } + phid->dataBufferNormalized[dataWriter] = newtime; + } + //low time + else + { + int newtime = lowFinals[0]; + double matchPercent = 1; + for(i = 0; idataBuffer[dataWriter], lowFinals[i]); + if (diff <= 0.30 && diff < matchPercent) + { + newtime = lowFinals[i]; + matchPercent = diff; + } + } + phid->dataBufferNormalized[dataWriter] = newtime; + } + + high_low ^= 1; + dataWriter = ((dataWriter + 1) & IR_DATA_ARRAY_MASK); + } + + //strip the header - if 1st pulse and/or the 1st space is unique, we assume first pulse/space is a header + dataReader = (phid->dataReadPtr + 1) & IR_DATA_ARRAY_MASK; + for(i=0;idataBufferNormalized[dataReader] == highFinals[i] && highFinalsCounts[i] == 1) + { + dataReader = (phid->dataReadPtr + 3) & IR_DATA_ARRAY_MASK; + } + + //put the code data into the code array + i=0; + //include the trailing bit! + //dataWriter = ((dataWriter - 1) & IR_DATA_ARRAY_MASK); + while(dataWriter != dataReader) + { + code_data[i++] = phid->dataBufferNormalized[dataReader]; + dataReader = ((dataReader + 1) & IR_DATA_ARRAY_MASK); + } + + //adjust the read pointer + phid->dataReadPtr = dataReader; + + //try to decode + if(!decode_data(code_data, i, decoded_data, &decodedcount, &codeInfo)) + { + //repeat logic + int repeat = PFALSE; + if(!memcmp(phid->lastCode, decoded_data, decodedcount) + && phid->lastGap <= IR_MAX_GAP_LENGTH && phid->lastGap >= IR_MIN_GAP_LENGTH + && phid->lastCodeInfo.bitCount == codeInfo.bitCount + && phid->lastCodeInfo.encoding == codeInfo.encoding) + repeat = PTRUE; + + //send out the code event! + FIRE(Code, decoded_data, decodedcount, codeInfo.bitCount, repeat); + //LOG(PHIDGET_LOG_DEBUG, "Encoding: %d",encoding); + + //store to last code + ZEROMEM(phid->lastCode, sizeof(phid->lastCode)); + memcpy(phid->lastCode, decoded_data, decodedcount); + phid->lastCodeInfo.bitCount = codeInfo.bitCount; + phid->lastCodeInfo.encoding = codeInfo.encoding; + phid->lastRepeat = repeat; + phid->lastCodeKnown = PTRUE; + //TODO: add header + } + //probably a repeat code (ie NEC code) + else if((i == 1 || i == 3) && phid->lastCodeKnown && phid->lastGap <= IR_MAX_GAP_LENGTH && phid->lastGap >= IR_MIN_GAP_LENGTH) + { + int dataSize = (phid->lastCodeInfo.bitCount / 8) + ((phid->lastCodeInfo.bitCount % 8) ? 1 : 0); + //send out the code event! + FIRE(Code, phid->lastCode, dataSize, phid->lastCodeInfo.bitCount, PTRUE); + } + + //Done + return EPHIDGET_OK; +} + +PHIDGET21_API int CCONV codeInfoToString(CPhidgetIR_CodeInfoHandle codeInfo, char *string) +{ + int i; + unsigned char *codeInfoBytes = (unsigned char *)codeInfo; + for(i=0;ibitCount || codeInfo->bitCount > IR_MAX_CODE_BIT_COUNT) + return EPHIDGET_INVALIDARG; + + //TODO: we're choosing arbitrary 10kHz - 1MHz range here + if((codeInfo->carrierFrequency && codeInfo->carrierFrequency < 10000) || codeInfo->carrierFrequency > 1000000) + return EPHIDGET_INVALIDARG; + + //duty cycle between 10% and 50% - never allow higher then 50% + if((codeInfo->dutyCycle && codeInfo->dutyCycle < 10) || codeInfo->dutyCycle > 50) + return EPHIDGET_INVALIDARG; + + //default encoding + if(!codeInfo->encoding) + codeInfo->encoding = PHIDGET_IR_ENCODING_SPACE; + if(!codeInfo->length) + codeInfo->length = PHIDGET_IR_LENGTH_CONSTANT; + + //fill in other defaults based on encoding + switch(codeInfo->encoding) + { + case PHIDGET_IR_ENCODING_SPACE: + //we'll default to NEC coding + if(!codeInfo->zero[0] && !codeInfo->one[0]) + { + codeInfo->zero[0] = 560; + codeInfo->zero[1] = 560; + codeInfo->one[0] = 560; + codeInfo->one[1] = 1680; + if(!codeInfo->header[0]) + { + codeInfo->header[0] = 9000; + codeInfo->header[1] = 4500; + } + if(!codeInfo->repeat) + { + codeInfo->repeat[0] = 9000; + codeInfo->repeat[1] = 2250; + codeInfo->repeat[2] = 560; + } + if(!codeInfo->trail) + codeInfo->trail = 560; + if(!codeInfo->gap) + codeInfo->gap = 110000; + if(!codeInfo->carrierFrequency) + codeInfo->carrierFrequency = 38000; + } + if(!codeInfo->trail) + return EPHIDGET_INVALIDARG; + break; + case PHIDGET_IR_ENCODING_PULSE: + //default to SONY coding + if(!codeInfo->zero[0] && !codeInfo->one[0]) + { + codeInfo->zero[0] = 600; + codeInfo->zero[1] = 600; + codeInfo->one[0] = 1200; + codeInfo->one[1] = 600; + if(!codeInfo->header[0]) + { + codeInfo->header[0] = 2400; + codeInfo->header[1] = 600; + } + if(!codeInfo->gap) + codeInfo->gap = 45000; + if(!codeInfo->carrierFrequency) + codeInfo->carrierFrequency = 40000; + } + break; + case PHIDGET_IR_ENCODING_BIPHASE: + //no default here.. + break; + case PHIDGET_IR_ENCODING_RC5: + if(!codeInfo->zero[0] && !codeInfo->one[0]) + { + codeInfo->zero[0] = 889; + codeInfo->zero[1] = 889; + codeInfo->one[0] = 889; + codeInfo->one[1] = 889; + if(!codeInfo->gap) + codeInfo->gap = 114000; + if(!codeInfo->carrierFrequency) + codeInfo->carrierFrequency = 36000; + } + break; + case PHIDGET_IR_ENCODING_RC6: + if(!codeInfo->zero[0] && !codeInfo->one[0]) + { + codeInfo->zero[0] = 444; + codeInfo->zero[1] = 444; + codeInfo->one[0] = 444; + codeInfo->one[1] = 444; + if(!codeInfo->header[0]) + { + codeInfo->header[0] = 2666; + codeInfo->header[1] = 889; + } + if(!codeInfo->gap) + codeInfo->gap = 105000; + if(!codeInfo->carrierFrequency) + codeInfo->carrierFrequency = 36000; + } + break; + case PHIDGET_IR_ENCODING_UNKNOWN: + default: + return EPHIDGET_INVALIDARG; + } + + //fill in defaults for things that the user didn't specify + if(!codeInfo->carrierFrequency) + codeInfo->carrierFrequency = 38000; + if(!codeInfo->dutyCycle) + codeInfo->dutyCycle = 33; + if(!codeInfo->min_repeat) + codeInfo->min_repeat = 1; + + //make sure that other things are filled in + if(!codeInfo->zero[0]) + return EPHIDGET_INVALIDARG; + if(!codeInfo->one[0]) + return EPHIDGET_INVALIDARG; + if(!codeInfo->gap) + return EPHIDGET_INVALIDARG; + + dataSize = (codeInfo->bitCount / 8) + ((codeInfo->bitCount % 8) ? 1 : 0); + if(CPhidget_statusFlagIsSet(phid->phid.status, PHIDGET_REMOTE_FLAG)) + { + char *newVal = (char *)malloc(1024); + ZEROMEM(newVal, 1024); + //add codeInfo data + codeInfoToString(codeInfo, newVal); + //now add transmit data + byteArrayToString(data, dataSize, newVal+sizeof(CPhidgetIR_CodeInfo)*2); + ADDNETWORKKEY(Transmit, "%s", tempString); + free(newVal); + } + else + { + int time, i; + int dataSize = (codeInfo->bitCount / 8) + ((codeInfo->bitCount % 8) ? 1 : 0); + int dataBuffer[IR_DATA_ARRAY_SIZE]; + int dataBufferLength = IR_DATA_ARRAY_SIZE * sizeof(int); + int repcount = codeInfo->min_repeat; + unsigned char datatemp[IR_MAX_CODE_DATA_LENGTH]; + + memcpy(datatemp, data, dataSize); + + //send out the number of times required + while(repcount--) + { + //convert code into raw data array and send + dataBufferLength = IR_DATA_ARRAY_SIZE * sizeof(int); + if((retval = codeInfoToRawData(datatemp, *codeInfo, dataBuffer, &dataBufferLength, &time, PFALSE)) != EPHIDGET_OK) + return retval; + + if(codeInfo->length == PHIDGET_IR_LENGTH_CONSTANT) + time = codeInfo->gap - time; + else + time = codeInfo->gap; + + if((retval = sendRAWData(phid, dataBuffer, dataBufferLength, codeInfo->carrierFrequency, codeInfo->dutyCycle, time)) != EPHIDGET_OK) + return retval; + + memcpy(phid->lastSentCode, datatemp, dataSize); + + //toggle data + for(i=0;itoggle_mask[i]; + } + + //got here? success in sending + phid->lastSentCodeInfo = *codeInfo; + } + + return EPHIDGET_OK; +} + +PHIDGET21_API int CCONV CPhidgetIR_TransmitRaw(CPhidgetIRHandle phid, int *data, int dataLength, int carrierFrequency, int dutyCycle, int gap) +{ + int retval, time=0; + int rawData[IR_DATA_ARRAY_SIZE]; + int rawDataLength = IR_DATA_ARRAY_SIZE; + TESTPTR(phid) + TESTDEVICETYPE(PHIDCLASS_IR) + TESTATTACHED + + //needs to be uneven - start and end in a pulse + if((dataLength % 2) != 1) + return EPHIDGET_INVALIDARG; + + //TODO: we're choosing arbitrary 10kHz - 1MHz range here + if((carrierFrequency && carrierFrequency < 10000) || carrierFrequency > 1000000) + return EPHIDGET_INVALIDARG; + + //duty cycle between 10% and 50% - never allow higher then 50% + if((dutyCycle && dutyCycle < 10) || dutyCycle > 50) + return EPHIDGET_INVALIDARG; + + //defaults + if(!carrierFrequency) + carrierFrequency = 38000; + if(!dutyCycle) + dutyCycle = 33; + + if(dataLength>(1000/5)) + return EPHIDGET_INVALIDARG; + + if(CPhidget_statusFlagIsSet(phid->phid.status, PHIDGET_REMOTE_FLAG)) + { + char *newVal = (char *)malloc(1024); + ZEROMEM(newVal, 1024); + wordArrayToString(data, dataLength, newVal); + sprintf(newVal+dataLength*5, ",%d,%d,%d", carrierFrequency, dutyCycle, gap); + ADDNETWORKKEY(TransmitRaw, "%s", tempString); + free(newVal); + } + else + { + if((retval = RawTimeDataToRawData(data, dataLength, rawData, &rawDataLength, &time)) != EPHIDGET_OK) + return retval; + if((retval = sendRAWData(phid, rawData, rawDataLength, carrierFrequency ? carrierFrequency : 38000, dutyCycle ? dutyCycle : 33, gap)) != EPHIDGET_OK) + return retval; + } + + return EPHIDGET_OK; +} + +PHIDGET21_API int CCONV CPhidgetIR_TransmitRepeat(CPhidgetIRHandle phid) +{ + int retval; + int dataSize; + TESTPTR(phid) + TESTDEVICETYPE(PHIDCLASS_IR) + TESTATTACHED + + if(CPhidget_statusFlagIsSet(phid->phid.status, PHIDGET_REMOTE_FLAG)) + { + int newVal = phid->flip^1; + ADDNETWORKKEY(Repeat, "%d", flip); + SLEEP(25);//make sure we limit the repeat rate + } + else + { + dataSize = (phid->lastSentCodeInfo.bitCount / 8) + ((phid->lastSentCodeInfo.bitCount % 8) ? 1 : 0); + //assume that last code is valid + if(dataSize > 0) + { + int dataBuffer[IR_DATA_ARRAY_SIZE]; + int dataBufferLength = IR_DATA_ARRAY_SIZE * sizeof(int); + int time, i; + unsigned char datatemp[IR_MAX_CODE_DATA_LENGTH]; + + //get last data sent + memcpy(datatemp, phid->lastSentCode, dataSize); + + //toggle data + for(i=0;ilastSentCodeInfo.toggle_mask[i]; + + //construct the last code into a repeat code + dataBufferLength = IR_DATA_ARRAY_SIZE * sizeof(int); + if((retval = codeInfoToRawData(datatemp, phid->lastSentCodeInfo, dataBuffer, &dataBufferLength, &time, PTRUE)) != EPHIDGET_OK) + return retval; + + if(phid->lastSentCodeInfo.length == PHIDGET_IR_LENGTH_CONSTANT) + time = phid->lastSentCodeInfo.gap - time; + else + time = phid->lastSentCodeInfo.gap; + + //send the data + if((retval = sendRAWData(phid, dataBuffer, dataBufferLength, phid->lastSentCodeInfo.carrierFrequency, phid->lastSentCodeInfo.dutyCycle, time)) != EPHIDGET_OK) + return retval; + + //store last sent code + memcpy(phid->lastSentCode, datatemp, dataSize); + } + else + { + LOG(PHIDGET_LOG_WARNING, "Can't send a repeat until a code has been transmitted"); + return EPHIDGET_UNKNOWNVAL; + } + } + + return EPHIDGET_OK; +} + +//make sure that our raw data starts with a high and ends with a low +PHIDGET21_API int CCONV CPhidgetIR_getRawData(CPhidgetIRHandle phid, int *data, int *dataLength) +{ + int i; + TESTPTR(phid) + TESTDEVICETYPE(PHIDCLASS_IR) + TESTATTACHED + + //make sure length is even so we only send out data with starting space and ending pulse + if((*dataLength % 2) == 1) + (*dataLength)--; + + for(i=0;i<*dataLength;i++) + { + if(phid->userReadPtr == phid->dataWritePtr) + break; + + data[i] = phid->dataBuffer[phid->userReadPtr]; + phid->userReadPtr = (phid->userReadPtr + 1) & IR_DATA_ARRAY_MASK; + } + + //make sure i is even so that we don't end with a pulse + if((i % 2) == 1) + { + //negate the pulse if we added it + i--; + phid->userReadPtr = (phid->userReadPtr - 1) & IR_DATA_ARRAY_MASK; + } + + *dataLength = i; + + return EPHIDGET_OK; +} + +PHIDGET21_API int CCONV CPhidgetIR_getLastCode(CPhidgetIRHandle phid, unsigned char *data, int *dataLength, int *bitCount) +{ + TESTPTR(phid) + TESTDEVICETYPE(PHIDCLASS_IR) + TESTATTACHED + + if(!phid->lastCodeKnown) + return EPHIDGET_UNKNOWNVAL; + + { + int dataSize = (phid->lastCodeInfo.bitCount / 8) + ((phid->lastCodeInfo.bitCount % 8) ? 1 : 0); + + *bitCount = phid->lastCodeInfo.bitCount; + + if(*dataLength < dataSize) + { + *dataLength = dataSize; + return EPHIDGET_NOMEMORY; + } + *dataLength = dataSize; + + memcpy(data, phid->lastCode, dataSize); + } + + return EPHIDGET_OK; + +} + +PHIDGET21_API int CCONV CPhidgetIR_getLastLearnedCode(CPhidgetIRHandle phid, unsigned char *data, int *dataLength, CPhidgetIR_CodeInfo *codeInfo) +{ + TESTPTR(phid) + TESTDEVICETYPE(PHIDCLASS_IR) + TESTATTACHED + + if(!phid->lastLearnedCodeKnown) + return EPHIDGET_UNKNOWNVAL; + + { + int dataSize = (phid->lastLearnedCodeInfo.bitCount / 8) + ((phid->lastLearnedCodeInfo.bitCount % 8) ? 1 : 0); + + if(*dataLength < dataSize) + { + *dataLength = dataSize; + return EPHIDGET_NOMEMORY; + } + *dataLength = dataSize; + + memcpy(data, phid->lastLearnedCode, dataSize); + + *codeInfo = phid->lastLearnedCodeInfo; + } + + return EPHIDGET_OK; +} -- cgit v1.2.3