// Jan. 2021 // by paul, inspired / recycled from microchip code by SMIOT #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdbool.h> #include <math.h> #define MAX_PERIPHERAL 5 //Nombre max de peripheriques externes typedef struct{ char Type; //type du peripherique (0x01 accel, 0x02 gyro, 0x03 magneto, 0x04 temperature, 0x05 pressure, 0x06 light,...) char ID; //ID du peripherique char Range; //Range de la mesure (ex: 2G, 4G, 6G, 8G, 16G pour un accel) char Resolution; //Resolution de mesure du peripherique short Frequency; //Frequence d'echantillonage du peripherique }PERIPHERAL_CONFIGURATION; typedef struct{ int headerSize; //Taille du header ce champ exclu int versionNumber; char numberOfChan; char resolutionBits; int samplingFrequency; int dmaBlockSize; int sizeOfAdditionnalDataBuffer; char numberOfExternalPeripheral; int timeStampOfStart; PERIPHERAL_CONFIGURATION periphConfig[MAX_PERIPHERAL]; }HighBlueHeader; struct WaveHeader_s { char chunkId[4]; // Riff Wave Header int chunkSize; char format[4]; char subChunk1Id[4]; // Format Subchunk int subChunk1Size; short int audioFormat; short int numChannels; int sampleRate; int byteRate; short int blockAlign; short int bitsPerSample; //short int extraParamSize; char subChunk2Id[4]; // Data Subchunk int subChunk2Size; } WaveHeader_default = {{'R','I','F','F'}, 36, {'W','A','V','E'}, {'f','m','t',' '}, 16, 1, 0, 0, 0, 0, 0, {'d','a','t','a'}, 0}; typedef struct WaveHeader_s WaveHeader; void parseLogFileHeader(FILE* logfile, HighBlueHeader* hdr, int verbose){ // header parse fread(&hdr->headerSize, 4, 1, logfile); fread(&hdr->versionNumber, 2, 1, logfile); fread(&hdr->numberOfChan, 1, 1, logfile); fread(&hdr->resolutionBits, 1, 1, logfile); fread(&hdr->samplingFrequency, 4, 1, logfile); fread(&hdr->dmaBlockSize, 4, 1, logfile); fread(&hdr->sizeOfAdditionnalDataBuffer, 4, 1, logfile); fread(&hdr->numberOfExternalPeripheral, 1, 1, logfile); fread(&hdr->timeStampOfStart, 4, 1, logfile); if(verbose){ printf("header size : %d\n", hdr->headerSize); printf("version number : %d\n", hdr->versionNumber); printf("number of chans : %d\n", hdr->numberOfChan); printf("resolutionBits : %d\n", hdr->resolutionBits); printf("samplingFrequency : %d\n", hdr->samplingFrequency); printf("dmaBlockSize : %d\n", hdr->dmaBlockSize); printf("sizeOfAdditionnalDataBuffer : %d\n", hdr->sizeOfAdditionnalDataBuffer); printf("numberOfExternalPeripheral : %d\n", hdr->numberOfExternalPeripheral); printf("timeStampOfStart : %d\n", hdr->timeStampOfStart); } // load external periph config for(int i=0; i<hdr->numberOfExternalPeripheral; i++){ fread(&hdr->periphConfig[i].Type, 1, 1, logfile); fread(&hdr->periphConfig[i].ID, 1, 1, logfile); fread(&hdr->periphConfig[i].Range, 1, 1, logfile); fread(&hdr->periphConfig[i].Resolution, 1, 1, logfile); fread(&hdr->periphConfig[i].Frequency, 2, 1, logfile); } return; } short int toLittleEndian(short int val){ return val = ((val & 0x00FF)<<8) | ((val & 0xFF00)>>8); } void parseMPU(unsigned char* additionnalDataBlock, int size, bool verbose, FILE* mpuFile){ static int maxtimeStamp = 0; int i, timestamp; short int trameSize = 31, val; // fixed for now unsigned char* curData = additionnalDataBlock + 6; // first 6 bytes are for usb device if(verbose){ printf("MPU Range : %hdG\n", *(curData + 5 + 3)); printf("MPU Resolution : %hd\n", *(curData + 5 + 3 + 1)); printf("MPU Sampling Frequency : %hd\n", *(curData + 5 + 3 + 3)); } while(curData + trameSize + 6 < additionnalDataBlock + size){ if(!(curData[0]==0xFE && curData[1]==0x0A && curData[2]==0x0A && curData[5]==0x08)){ // skip trame if header is incorrect curData += trameSize + 6; continue; } curData += 3 + 2; // skip trame header, trame length timestamp = *((int*) (curData + 9)); timestamp = ((timestamp & 0xFF000000)>>24) | ((timestamp & 0x00FF0000)>>8) | ((timestamp & 0x0000FF00)<<8) | ((timestamp & 0x000000FF)<<24); if(timestamp > maxtimeStamp){ fprintf(mpuFile, "%d,", timestamp); //printf("%d\n", timestamp); // treat payload for(i=13; i<31; i+=2){ val = *((short int*) (curData + i)); val = ((val & 0x00FF)<<8) | ((val & 0xFF00)>>8); //printf("curData %x %x %hd", *(curData+i), *(curData + i +1), val); if(i<29){ fprintf(mpuFile, "%hd,", val); }else{ fprintf(mpuFile, "%hd\n", val); } } maxtimeStamp = timestamp; } curData += trameSize + 1; // shift of trame size + 1 byte of checksum } } int main(int argc, char* argv[]){ if(argc < 2){ printf("Script needs to be called with at least 1 arguments :\n\t input file name (.log file)\n\t(optionnal) output filename (.wav file)\n\t(optionnal) mpu filename (.csv file)\n\t(optionnal) verbose (1 / void)\n"); return 0; } HighBlueHeader hdr; FILE* logfile = fopen(argv[1], "rb"); if(logfile==NULL){ printf("Failed to open input file\n"); return 0; } // get file size fseek(logfile, 0, SEEK_END); long filesize = ftell(logfile); if(filesize == 0){ printf("skipped empty file : %s\n", argv[1]); return 0; } fseek(logfile, 0, SEEK_SET); int verbose = 0; if(argc==5){ verbose = *argv[4]=='1'; } // file opened successfully, we read the header parseLogFileHeader(logfile, &hdr, verbose); int resolutionBytes = hdr.resolutionBits/8; long dataBlockSampleSize = hdr.dmaBlockSize / ( hdr.numberOfChan * resolutionBytes); if(verbose){ printf("file size (bytes) %ld \n", filesize); printf("dataBlockSampleSize %ld\n", dataBlockSampleSize); } if(hdr.resolutionBits!=16 && hdr.resolutionBits!=24 && hdr.resolutionBits!=32){ printf("resolution %d not supported yet sorry\n", hdr.resolutionBits); return 0; } // move to start of the data fseek(logfile, hdr.headerSize + 4, SEEK_SET); FILE* wavfile;// open wav file if(argc>2){ wavfile = fopen(argv[2], "wb"); }else{ strcpy(argv[1] + strlen(argv[1])-3, "wav\0"); wavfile = fopen(argv[1], "wb"); } if(wavfile==NULL){ printf("Failed to open wav output file\n"); return 0; } WaveHeader whdr = WaveHeader_default; whdr.numChannels = hdr.numberOfChan; whdr.sampleRate = hdr.samplingFrequency; whdr.bitsPerSample = hdr.resolutionBits; whdr.byteRate = whdr.sampleRate * whdr.numChannels * resolutionBytes; whdr.blockAlign = whdr.numChannels * resolutionBytes; whdr.chunkSize = 36 + (filesize - hdr.headerSize - 4) / (hdr.dmaBlockSize + hdr.sizeOfAdditionnalDataBuffer) * hdr.numberOfChan * dataBlockSampleSize * resolutionBytes; whdr.subChunk2Size = whdr.chunkSize-36; fwrite(&whdr, sizeof(WaveHeader), 1, wavfile); FILE* mpuFile = NULL; // open mpu file if(argc>3){ mpuFile = fopen(argv[3], "w+"); if(mpuFile==NULL){ printf("Failed to open mpu output file\n"); return 0; } } // }else{ // strcpy(argv[1] + strlen(argv[1])-4, "_mpu.csv\0"); // mpuFile = fopen(argv[1], "w+"); // argv[1][strlen(argv[1])-8] = '\0'; // } char* dmaBlock = (char*) malloc(hdr.dmaBlockSize); char* additionnalDataBlock = (char*) malloc(hdr.sizeOfAdditionnalDataBuffer); char* samples = (char*) malloc(hdr.numberOfChan * resolutionBytes); int ichan, isample; long pos = 0; bool isFirst = true; // read each dataBlock do{ fread(additionnalDataBlock, hdr.sizeOfAdditionnalDataBuffer, 1, logfile); if(mpuFile != NULL){ parseMPU(additionnalDataBlock, hdr.sizeOfAdditionnalDataBuffer, isFirst && verbose, mpuFile); isFirst = false; } fread(dmaBlock, hdr.dmaBlockSize, 1, logfile); for(isample=0; isample<dataBlockSampleSize; isample++){ for(ichan=0; ichan<hdr.numberOfChan; ichan++){ memcpy(samples + ichan * resolutionBytes, dmaBlock + (ichan * dataBlockSampleSize + isample) * resolutionBytes, resolutionBytes); } fwrite(samples, resolutionBytes, hdr.numberOfChan, wavfile); } printf("\r %s : ", argv[1]); pos = ftell(logfile); printf(" %ld%%", pos*100/filesize); }while(pos < filesize - 1); printf("\r\n"); fclose(wavfile); fclose(logfile); if(mpuFile!=NULL){ fclose(mpuFile); } return 0; }