CFile & CStdioFile produces different results in writing & reading - file
To my understanding, when creating with typeBinary flag, CFile & CStdioFile should works indentically, except the latter is buffering the data so has a better performance.
So I write the following codes to confirm this:
ULONGLONG GetRand(ULONGLONG uMax)
{
UINT uValue;
if (rand_s(&uValue) == 0)
return (ULONGLONG)(((double)uValue / (double)UINT_MAX) * uMax);
else
return 0;
}
void CheckOffset(CFile& File1, CFile& File2)
{
ULONGLONG uOffset1, uOffset2;
CString strMsg;
uOffset1 = File1.GetPosition();
uOffset2 = File2.GetPosition();
if (uOffset1 != uOffset2)
{
strMsg.Format(_T("Difference offset. Offset1 = %I64u. Offset2 = %I64u."), uOffset1, uOffset2);
AfxMessageBox(strMsg);
}
}
void CheckLength(CFile& File1, CFile& File2)
{
ULONGLONG uLength1, uLength2;
CString strMsg;
uLength1 = File1.GetLength();
uLength2 = File2.GetLength();
if (uLength1 != uLength2)
{
strMsg.Format(_T("Difference length. Length1 = %I64u. Length2 = %I64u."), uLength1, uLength2);
AfxMessageBox(strMsg);
}
}
void CheckSeek(CFile& File1, CFile& File2, ULONGLONG uOffset)
{
ULONGLONG uOffset1, uOffset2;
CString strMsg;
uOffset1 = File1.Seek(uOffset, CFile::begin);
uOffset2 = File2.Seek(uOffset, CFile::begin);
if (uOffset1 != uOffset2)
{
strMsg.Format(_T("Difference seek results. Offset1 = %I64u. Offset2 = %I64u."), uOffset1, uOffset2);
AfxMessageBox(strMsg);
}
}
void CheckRead(CFile& File1, CFile& File2, UINT uSize)
{
BYTE lpBuf1[4096], lpBuf2[4096];
UINT uRead1, uRead2;
CString strMsg;
// Read buffer from file1 & file2
uRead1 = File1.Read(lpBuf1, uSize);
uRead2 = File2.Read(lpBuf2, uSize);
if ((uRead1 != uRead2) || (memcmp(lpBuf1, lpBuf2, uRead1) != 0))
{
strMsg.Format(_T("Difference read results. uRead1 = %u. uRead2 = %u."), uRead1, uRead2);
AfxMessageBox(strMsg);
}
}
void CTestStdioFile64Dlg::OnBnClickedButton1()
{
// TODO: Add your control notification handler code here
CFile File1;
CStdioFile File2;
UINT uSize;
BYTE lpBuf[4096];
CString strMsg;
if (File1.Open(_T("F:\\Temp\\Test1.dat"), CFile::modeCreate | CFile::modeReadWrite | CFile::shareExclusive | CFile::typeBinary))
{
if (File2.Open(_T("F:\\Temp\\Test2.dat"), CFile::modeCreate | CFile::modeReadWrite | CFile::shareExclusive | CFile::typeBinary))
{
CheckOffset(File1, File2);
CheckLength(File1, File2);
// Write data
for (UINT uIndex = 0; uIndex < 20000; uIndex ++)
{
// Generate a randome size for write
uSize = (UINT)GetRand(4096);
// Generate buffer with random data
for (UINT j = 0; j < uSize; j++)
lpBuf[j] = (BYTE)GetRand(255);
// Write buffer to file1 & file2
File1.Write(lpBuf, uSize);
File2.Write(lpBuf, uSize);
File1.Flush();
File2.Flush();
CheckOffset(File1, File2);
CheckLength(File1, File2);
// Seek to a randome location
CheckSeek(File1, File2, GetRand(File1.GetLength()));
// Generate a randome size for read
uSize = (UINT)GetRand(4096);
CheckRead(File1, File2, uSize);
CheckOffset(File1, File2);
}
File2.Close();
}
File1.Close();
}
}
To my surprise, in the test process, there are many CFileException raised because CStdioFile::Write will write out less amount of data than expected.
Also there are many different read data reported.
Why?
When I run your code in Debug mode, I am getting the following ASSERT.
"Flush between consecutive read and write.",
!stream.has_any_of(_IOREAD)
The reason is following:
From C Standard documentation:
(Page 306, 7.21.5.3).
When a file is opened with update mode ('+' as the second or third
character in the above list of mode argument values), both input and
output may be performed on the associated stream. However, output
shall not be directly followed by input without an intervening call to
the fflush function or to a file positioning function (fseek, fsetpos,
or rewind), and input shall not be directly followed by output without
an intervening call to a file positioning function, unless the input
operation encounters endof-file.
In your code, you are calling for CheckRead function at the end of the loop, but calling for File1.Write and File2.Write in the next iteration without calling for fseek/flush.
As a quick fix, you can add the following lines at the bottom of your CheckRead function:
File1.Seek(0, SEEK_CUR);
File2.Seek(0, SEEK_CUR);
Related
scanf register new conversion specifier
I wrote this week an extension for the printf family of functions to accept %b to print binary. For that, I used the function register_printf_specifier(). Now I wonder if I can do the same in the scanf family of functions to accept a binary input and write it into a variable. Is there any extension that allows me to do that?
TL;DR: No. At least no when using glibc. I've downloaded recent glibc version: % wget https://ftp.gnu.org/gnu/glibc/glibc-2.29.tar.gz % tar -xzf glibc-2.29.tar.gz And grep'ed find, searching for random scanf family function that came to my mind - in this case, it was vfscanf: % find | grep "vfscanf" From my experience I know that real implementations are somewhere in -internal, yet I looked through output: ./stdio-common/iovfscanf.c ./stdio-common/isoc99_vfscanf.c ./stdio-common/vfscanf-internal.c ./stdio-common/vfscanf.c ./sysdeps/ieee754/ldbl-opt/nldbl-iovfscanf.c ./sysdeps/ieee754/ldbl-opt/nldbl-isoc99_vfscanf.c ./sysdeps/ieee754/ldbl-opt/nldbl-vfscanf.c And decided to check ./stdio-common/vfscanf.c, that in fact contained stub to the internal function: % cat ./stdio-common/vfscanf.c int ___vfscanf (FILE *s, const char *format, va_list argptr) { return __vfscanf_internal (s, format, argptr, 0); } Going forward, I've looked thru the file, and reached format parser: % cat ./stdio-common/vfscanf-internal.c | head -n 1390 | tail -n 20 } break; case L_('x'): /* Hexadecimal integer. */ case L_('X'): /* Ditto. */ base = 16; goto number; case L_('o'): /* Octal integer. */ base = 8; goto number; case L_('u'): /* Unsigned decimal integer. */ base = 10; goto number; case L_('d'): /* Signed decimal integer. */ base = 10; flags |= NUMBER_SIGNED; goto number; I've looked at the end of file, and found some finishing case label: % cat ./stdio-common/vfscanf-internal.c | tail -n 60 ++done; } } break; case L_('p'): /* Generic pointer. */ base = 16; /* A PTR must be the same size as a `long int'. */ flags &= ~(SHORT|LONGDBL); if (need_long) flags |= LONG; flags |= READ_POINTER; goto number; default: /* If this is an unknown format character punt. */ conv_error (); } } /* The last thing we saw int the format string was a white space. Consume the last white spaces. */ if (skip_space) { do c = inchar (); while (ISSPACE (c)); ungetc (c, s); } errout: /* Unlock stream. */ UNLOCK_STREAM (s); scratch_buffer_free (&charbuf.scratch); if (__glibc_unlikely (done == EOF)) { if (__glibc_unlikely (ptrs_to_free != NULL)) { struct ptrs_to_free *p = ptrs_to_free; while (p != NULL) { for (size_t cnt = 0; cnt < p->count; ++cnt) { free (*p->ptrs[cnt]); *p->ptrs[cnt] = NULL; } p = p->next; ptrs_to_free = p; } } } else if (__glibc_unlikely (strptr != NULL)) { free (*strptr); *strptr = NULL; } return done; } And the code that finished the function. This means, all format specifiers are constant for one of scanf-family functions, and this implies that you can't register new handler without messing with the large clusterf..k in glibc source (that of course won't be portable).
Read binary (.hgt) file in Swift (migrate code from c++ to swift)
I need to read elevation data from a binary .hgt file in Swift. I have found this result for c, but I can not migrate it to Swift. #include <stdio.h> #define SIZE 1201 signed short int matrix[SIZE][SIZE] = {0}; int main(int argc, const char * argv[]) { FILE *fp = fopen("N49E013.hgt", "rb"); unsigned char buffer[2]; for (int i = 0; i < SIZE; ++i) { for (int j = 0; j < SIZE; ++j) { if (fread(buffer, sizeof(buffer), 1, fp) != 1) { printf("Error reading file!\n"); system("PAUSE"); return -1; } matrix[i][j] = (buffer[0] << 8) | buffer[1]; } } fclose(fp); }
#define SIZE 1201 This defines a constant named 'SIZE', so do that: let size = 1201 next: FILE *fp = fopen("N49E013.hgt", "rb"); This opens a file for reading. We can do that. Close the file in a 'defer' block, so that no matter what, the file gets closed when we're done. // change the path below to the correct path let handle = try FileHandle(forReadingFrom: URL(fileURLWithPath: "/path/to/N49E013.hgt")) defer { handle.closeFile() } Now, to construct the matrix. We want to create size number of arrays, each of which has size elements, read from the file. The original used two nested for loops, but Swift supports functional programming constructs, which we can use to do this a bit more elegantly: let matrix = try (0..<size).map { _ in try (0..<size).map { _ -> Int in // Unfortunately, FileHandle doesn't have any decent error-reporting mechanism // other than Objective-C exceptions. // If you need to catch errors, you can use fread as in the original, // or use an Objective-C wrapper to catch the exceptions. let data = handle.readData(ofLength: 2) if data.count < 2 { throw CocoaError(.fileReadCorruptFile) } return (Int(data[0]) << 8) | Int(data[1]) } } Think that ought to do it.
I was implementing the same problem recently but found out solution provided by Charles Srstka is bit slow. It takes about 10 seconds to load one file on Late 2016 15" MBP. I tweaked it a bit and made it about 50x faster using direct access to memory and reading it by rows instead of 2 bytes. static let size = 1201 static func read(from path: String) throws -> [[UInt16]] { let handle = try FileHandle(forReadingFrom: URL(fileURLWithPath: path)) defer { handle.closeFile() } // Calculate all the necessary values let unitSize = MemoryLayout<UInt16>.size let rowSize = size * unitSize let expectedFileSize = size * rowSize // Get fileSize let fileSize = handle.seekToEndOfFile() // Check file size guard fileSize == expectedFileSize else { throw CocoaError(.fileReadCorruptFile) } // Go back to the start handle.seek(toFileOffset: 0) // Iterate let matrix: [[UInt16]] = (0..<size).map { _ in // Read a row let data = handle.readData(ofLength: rowSize) // With bytes... let row: [UInt16] = data.withUnsafeBytes { (bytes: UnsafePointer<UInt16>) -> [UInt16] in // Get the buffer. Count isn't using rowSize because it calculates number of bytes based on data type let buffer = UnsafeBufferPointer<UInt16>(start: bytes, count: size) // Create an array return Array<UInt16>(buffer) } // Return row, swapping from Little to Big endian return row.map { CFSwapInt16HostToBig($0) } } return matrix }
Resampling PCM file data with soxr and libsndfile crashes
I'm building an app that, in part, needs to resample any input PCM audio file that isn't 44100Hz to 44.1 (or at least make a best effort to do so). To handle the resampling I'm using soxr. soxr has no dependencies and is lightweight, which is ideal in this case, but it offers no native file I/O. I have very limited experience with IO streams in C, so I'm hitting a wall. The app is being designed modularly, so I need the resample process to create an output file that can then be passed on to other processors, rather than simply dealing with the output stream directly. In order to create that output file, I'm trying to take the data generated by the soxr resampling process, and pass it to libsndfile, which should be able to write the audio out to a file. Below is an extremely verbose explanation of where I'm at, though I'm at a loss for why it's crashing. I suspect it has something to do with how buffers are being allocated and used. (Note: The input file is being read with sndfile prior to this code) (Here's a single gist of the entire thing) Basic resampler options // Use "high quality" resampling unsigned int q_recipe = SOXR_HQ; // No unsigned long q_flags = 0; // Create the q_spec soxr_quality_spec_t q_spec = soxr_quality_spec(q_recipe, q_flags); Map the sndfile format to a soxr format soxr_datatype_t itype; // Get the SFINFO format int iformat = self.inputFileInfo.format; // Set the soxr itype to the corresponding format if ((iformat & SF_FORMAT_FLOAT) == SF_FORMAT_FLOAT) { itype = SOXR_FLOAT32_S; } else if ((iformat & SF_FORMAT_DOUBLE) == SF_FORMAT_DOUBLE) { itype = SOXR_FLOAT64_S; } else if ((iformat & SF_FORMAT_PCM_32) == SF_FORMAT_PCM_32) { itype = SOXR_INT32_S; } else { itype = SOXR_INT16_S; } Setup soxr IO spec // Always want the output to match the input soxr_datatype_t otype = itype; soxr_io_spec_t io_spec = soxr_io_spec(itype, otype); Threading // A single thread is fine soxr_runtime_spec_t runtime_spec = soxr_runtime_spec(1); Construct the resampler soxr_error_t error; // Input rate can be read from the SFINFO double const irate = self.inputFileInfo.samplerate; // Output rate is defined elsewhere, but this generally = 44100 double const orate = self.task.resampler.immutableConfiguration.targetSampleRate; // Channel count also comes from SFINFO unsigned chans = self.inputFileInfo.channels; // Put it all together soxr_t soxr = soxr_create(irate, orate, chans, &error, &io_spec, &q_spec, &runtime_spec); Read, resample & write I'm not really confident in any of the following code, but I've triple checked the math and everything seems to meet the expectations of the libraries' APIs. // Frames in sndfile are called Samples in soxr // One frame is 1 item per channel // ie frame_items = 1 item * channels size_t const iframeitems = (1 * chans); // item size is the data type size of the input type // size_t iitemsize; if ((iformat & SF_FORMAT_FLOAT) == SF_FORMAT_FLOAT) { iitemsize = sizeof(Float32); } else if ((iformat & SF_FORMAT_DOUBLE) == SF_FORMAT_DOUBLE) { iitemsize = sizeof(Float64); } else if ((iformat & SF_FORMAT_PCM_32) == SF_FORMAT_PCM_32) { iitemsize = sizeof(int32_t); } else { iitemsize = sizeof(int16_t); } // frame size is item size * items per frame (channels) // eg for 2 channel 16 bit, frame size = 2 * 2 size_t const iframesize = (iframeitems * iitemsize); // Number of frames to read (arbitrary) sf_count_t const ireqframes = 1024; // Size of the buffer is number of frames * size per frame size_t const ibufsize = iframesize * ireqframes; void *ibuf = malloc(ibufsize); // Output ////////////////////////////// // These match the input size_t const oframeitems = iframeitems; size_t const oitemsize = iitemsize; // frame size is item size * items per frame (channels) size_t const oframesize = (oframeitems * oitemsize); // Number of frames expected after resampling // eg // orate = 44100 // irate = 48000 // ireqframe = 1024 // expect fewer frames (downsample) // (44100 / 4800) * 1024 = 940.8 // Add 0.5 to deal with rounding? sf_count_t const oexpframes = (ireqframes * (orate / irate)) + 0.5; // Size of the buffer is number of frames * size per frame size_t const obufsize = oframesize * oexpframes; void *obuf = malloc(obufsize); // Go ////////////////////////////// size_t total_resample_output_frame_count = 0; size_t need_input = 1; sf_count_t num_frames_written = 0; do { sf_count_t num_frames_read = 0; size_t actual_resample_output_samples = 0; // Read the input file based on its type // num_frames_read should be 1024 if (otype == SOXR_INT16_S || otype == SOXR_INT32_S) { num_frames_read = sf_readf_int(self.inputFile, ibuf, ireqframes); } else if (otype == SOXR_FLOAT32_S) { num_frames_read = sf_readf_float(self.inputFile, ibuf, ireqframes); } else { num_frames_read = sf_readf_double(self.inputFile, ibuf, ireqframes); } // If there were no frames left to read we're done if (num_frames_read == 0) { // passing NULL input buffer to soxr_process indicates End-of-input ibuf = NULL; need_input = 0; } // Run the resampling on frames read from the input file error = soxr_process(soxr, ibuf, num_frames_read, NULL, obuf, oexpframes, &actual_resample_output_samples); total_resample_output_frame_count += actual_resample_output_samples; // Write the resulting data to output file // num_frames_written should = actual_resample_output_samples if (otype == SOXR_INT16_S || otype == SOXR_INT32_S) { num_frames_written = sf_writef_int(self.outputFile, obuf, actual_resample_output_samples); } else if (otype == SOXR_FLOAT32_S) { num_frames_written = sf_writef_float(self.outputFile, obuf, actual_resample_output_samples); } else { num_frames_written = sf_writef_double(self.outputFile, obuf, actual_resample_output_samples); } } while (!error && need_input); soxr_delete(soxr); free(obuf), free(ibuf); This gives and EXC_BAD_ACCESS on soxr_process. I have no idea what else to try at this point.
The _S in data types like SOXR_INT32_S mean that you're using split channels, and from the example 4-split-channels.c it seems that in that case you need to pass an array of pointers, one for each channel. However, in the code above you just pass a single allocated block of memory so I'm guessing you're expecting interleaved channel data. Perhaps you can try changing the _S to _I.
loop over input in C-code and write data to different files using terminal at once
I wrote a C code which extracts data from a binary file which has size around 1 GB. There are 101 (0 to 100)configurations and the C code extracts data for a selected configuration and writes the output in a file. To compile the C code, I give user defined configuration number like this in the terminal: gcc binary2textperconfig.c -o f.out ./f.out proton-p000-1.bin out1.txt Then it asks for configuration number: Enter the configuration number: After that the data is written in file "out0.txt". Now I want to run this code for all 101 configurations and write those data to files "out0.txt", "out1.txt",...., "out100.txt" etc. I don't know how to do this without typing the configuration numbers 101 times in the terminal. Could any one please help me? Here is my C-code: #include <stdio.h> #include<complex.h> #include<math.h> #include <stdlib.h> #include <gsl/gsl_sf_gamma.h> #include <gsl/gsl_matrix.h> typedef double complex dcomplex; //Data is converted to Bigendian using io-general double constructfloat(char bytes[sizeof(double)/2], int order) { double dRes; char *pc; int i; if (order == 0) for(i=0, pc = (char*) &dRes; i<=sizeof(double)-1 ; i++, pc++) (*pc) = bytes[i]; else for(i=sizeof(double)-1, pc = (char*) &dRes; i>=0; i--, pc++) (*pc) = bytes[i]; return dRes; } int main(int argc, char *argv[]){ int configcount = 101; int mcount = 14; int tcount = 64; int d1count = 4; int d2count = 4; int pcount = 45; int userci; int usermi; int userpi; // number of complex numbers per configuration int unitcountperconfig =(mcount*tcount*d1count*d2count*pcount); // initialize loop index variables int ci = 0; //config int mi = 0; //mass int ti = 0; int d1i = 0; int d2i = 0; int pi = 0; //momentum // for holding the result of read operation ( how many units have been read) int result; // for holding the data read from file char * cbuff; // input file handle from where binary data is read FILE * fin = fopen(argv[1],"rb"); // if the input file cannot be read for reading, close opened file handles, show an error message to the user, and exit if (fin==NULL) { fputs ("Error opening input file\n",stderr); exit (1); } FILE * fout = fopen(argv[2],"wt"); // if the output file cannot be opened for writing, close opened file handles, show an error message to the user, and exit if (fout==NULL) { fclose(fin); fputs ("Error opening output file\n",stderr); exit (1); } // take input from the user // take input from the user printf("Enter the configuration number: "); scanf("%d",&userci); // allocate memory to contain the chunk of data for a time slice: cbuff = (char*)malloc(sizeof(dcomplex)*unitcountperconfig ); // show error message and exit if memory allocation failed if(cbuff == NULL) { fputs("Buffer allocation failed.", stderr); exit(1); } // variable to hold a complex number read from the file dcomplex aComplexNumber; dcomplex sumpertimeslice[tcount]; // loop on time slices for( ci = 0; ci< configcount ; ci++){ // index of the complex number being read unsigned int cNumberIdx = 0; // debugging message printf("reading data for configuration: %d\n",ci); // perform read operation to read the desired chunk of data result = fread(cbuff, sizeof(char), sizeof(dcomplex)*unitcountperconfig, fin ); // if size of data successfully read is not equal to what we wanted to read, notify the user and exit if (result != sizeof(dcomplex)*unitcountperconfig) { fputs ("data reading error\n",stderr); exit (3); } double realP; double imagP;// variable to hold real and imaginary part of the complex number double realPSum; double imagPSum;// variable to hold sum of real and sum of imaginary part of the current sum per time slice for (mi =0; mi< mcount ; mi++){ for (ti =0; ti< tcount ; ti++){ // array to hold trace for each time slice sumpertimeslice[ti] = 0.0 + 0.0*_Complex_I; for (d1i =0; d1i < d1count ; d1i++){ for (d2i =0; d2i < d2count ; d2i++){ for (pi =0; pi < pcount ; pi++){ aComplexNumber = constructfloat( &cbuff[cNumberIdx], 0 ) + constructfloat( &cbuff[cNumberIdx+ ((int)sizeof(dcomplex))/2 ], 0 )*_Complex_I; if (ci== userci) { cNumberIdx += (int)sizeof(dcomplex); if (cimag(aComplexNumber)>0) {fprintf( fout, "%d,%d,%d,%d,%d,%d,%e+%ei\n" ,ci+1, mi+1,ti+1, d1i+1,d2i+1,pi+1,creal( aComplexNumber ),cimag( aComplexNumber ) );} else {fprintf( fout, "%d,%d,%d,%d,%d,%d,%e%ei\n" ,ci+1, mi+1,ti+1, d1i+1,d2i+1,pi+1,creal( aComplexNumber ),cimag( aComplexNumber ) );} } } } } } } } // free the allocated memory free(cbuff); // close the opened file handles fclose(fin); fclose(fout); //fclose(complexNumberFileP); }
Use the seq utility to generate a list of number between 0 & 100, and send it as a string to stdin. for CNUMBER in $(seq 0 100); do ./f.out proton-p000-1.bin out${CNUMBER}.txt <<< "${CNUMBER}" done or for CNUMBER in $(seq 0 100); do echo $CNUMBER | ./f.out proton-p000-1.bin out${CNUMBER}.txt done
Parsing code for GPS NMEA string
i am trying to parse the incoming GPGGA NMEA GPS string using Arduino uno and below code. What i am trying to do is that i am using only GPGGA NMEA string to get the values of Latitude, longitude and altitude.In my below code, i had put certain checks to check if incoming string is GPGGA or not, and then store the further string in a array which can be further parsed suing strtok function and all the 3 GPS coordinates can be easily find out. But i am unable to figure out how to store only GPGGA string and not the further string.I am using a for loop but it isn't working. I am not trying to use any library.I had came across certain existing codes like this. Here is the GPGGA string information link i am trying to have following functionlity i) Check if incoming string is GPGGA ii) If yes, then store the following string upto EOL or upto * (followed by checksum for the array) in a array, array length is variable(i am unable to find out solution for this) iii) Then parse the stored array(this is done, i tried this with a different array) #include <SoftwareSerial.h> SoftwareSerial mySerial(10,11); // 10 RX / 11 TX void setup() { Serial.begin(9600); mySerial.begin(9600); } void loop() { uint8_t x; char gpsdata[65]; if((mySerial.available())) { char c = mySerial.read(); if(c == '$') {char c1 = mySerial.read(); if(c1 == 'G') {char c2 = mySerial.read(); if(c2 == 'P') {char c3 = mySerial.read(); if(c3 == 'G') {char c4 = mySerial.read(); if(c4 == 'G') {char c5 = mySerial.read(); if(c5 == 'A') {for(x=0;x<65;x++) { gpsdata[x]=mySerial.read(); while (gpsdata[x] == '\r' || gpsdata[x] == '\n') { break; } } } else{ Serial.println("Not a GPGGA string"); } } } } } } } Serial.println(gpsdata); } Edit 1: Considering Joachim Pileborg, editing the for loop in the code. I am adding a pic to show the undefined output of the code. Input for the code: $GPGGA,092750.000,5321.6802,N,00630.3372,W,1,8,1.03,61.7,M,55.2,M,,*76 $GPGSA,A,3,10,07,05,02,29,04,08,13,,,,,1.72,1.03,1.38*0A $GPGSV,3,1,11,10,63,137,17,07,61,098,15,05,59,290,20,08,54,157,30*70 $GPGSV,3,2,11,02,39,223,19,13,28,070,17,26,23,252,,04,14,186,14*79 $GPGSV,3,3,11,29,09,301,24,16,09,020,,36,,,*76 $GPRMC,092750.000,A,5321.6802,N,00630.3372,W,0.02,31.66,280511,,,A*43 $GPGGA,092751.000,5321.6802,N,00630.3371,W,1,8,1.03,61.7,M,55.3,M,,*75 $GPGSA,A,3,10,07,05,02,29,04,08,13,,,,,1.72,1.03,1.38*0A $GPGSV,3,1,11,10,63,137,17,07,61,098,15,05,59,290,20,08,54,157,30*70 $GPGSV,3,2,11,02,39,223,16,13,28,070,17,26,23,252,,04,14,186,15*77 $GPGSV,3,3,11,29,09,301,24,16,09,020,,36,,,*76 $GPRMC,092751.000,A,5321.6802,N,00630.3371,W,0.06,31.66,280511,,,A*45
After a quick check of the linked article on the NMEA 0183 protocol, this jumped out at me: <CR><LF> ends the message. This means, that instead of just read indiscriminately from the serial port, you should be looking for that sequence. If found, you should terminate the string, and break out of the loop. Also, you might want to zero-initialize the data string to begin with, to easily see if there actually is any data in it to print (using e.g. strlen).
You could use some functions from the C library libnmea. Theres functions to split a sentence into values by comma and then parse them.
Offering this as a suggestion in support of what you are doing... Would it not be useful to replace all of the nested if()s in your loop with something like: EDIT added global string to copy myString into once captured char globalString[100];//declare a global sufficiently large to hold you results void loop() { int chars = mySerial.available(); int i; char *myString; if (chars>0) { myString = calloc(chars+1, sizeof(char)); for(i=0;i<chars;i++) { myString[i] = mySerial.read(); //test for EOF if((myString[i] == '\n') ||(myString[i] == '\r')) { //pick this... myString[i]=0;//strip carriage - return line feed(or skip) //OR pick this... (one or the other. i.e.,I do not know the requirements for your string) if(i<chars) { myString[i+1] = mySerial.read() //get remaining '\r' or '\n' myString[i+2]=0;//add null term if necessary } break; } } if(strstr(myString, "GPGGA") == NULL) { Serial.println("Not a GPGGA string"); //EDIT strcpy(globalString, "");//if failed, do not want globalString populated } else { //EDIT strcpy(globalString, myString); } } //free(myString) //somewhere when you are done with it } Now, the return value from mySerial.available() tells you exactly how many bytes to read, you can read the entire buffer, and test for validity all in one.
I have a project that will need to pull the same information out of the same sentence. I got this out of a log file import serial import time ser = serial.Serial(1) ser.read(1) read_val = ("nothing") gpsfile="gpscord.dat" l=0 megabuffer='' def buffThis(s): global megabuffer megabuffer +=s def buffLines(): global megabuffer megalist=megabuffer.splitlines() megabuffer=megalist.pop() return megalist def readcom(): ser.write("ati") time.sleep(3) read_val = ser.read(size=500) lines=read_val.split('\n') for l in lines: if l.startswith("$GPGGA"): if l[:len(l)-3].endswith("*"): outfile=open('gps.dat','w') outfile.write(l.rstrip()) outfile.close() readcom() while 1==1: readcom() answer=raw_input('not looping , CTRL+C to abort') The result is this: gps.dat $GPGGA,225714.656,5021.0474,N,00412.4420,W,0,00,50.0,0.0,M,18.0,M,0.0,0000*5B
Using "malloc" every single time you read a string is an enormous amount of computational overhead. (And didn't see the corresponding free() function call. Without that, you never get that memory back until program termination or system runs out of memory.) Just pick the size of the longest string you will ever need, add 10 to it, and declare that your string array size. Set once and done. There are several C functions for getting substrings out of a string, strtok() using the coma is probably the least overhead. You are on an embedded microcontroller. Keep it small, keep overhead down. :)
#include <stdio.h> #include <string.h> #define GNSS_HEADER_LENGTH 5 #define GNSS_PACKET_START '$' #define GNSS_TOKEN_SEPARATOR ',' #define bool int #define FALSE 0 #define TRUE 1 //To trim a string contains \r\n void str_trim(char *str){ while(*str){ if(*str == '\r' || *str == '\n'){ *str = '\0'; } str++; } } /** * To parse GNSS data by header and the index separated by comma * * $GPGSV,1,1,03,23,39,328,30,18,39,008,27,15,33,035,33,1*5A * $GNRMC,170412.000,V,,,,,,,240322,,,N,V*2D * $GNGGA,170412.000,,,,,0,0,,,M,,M,,*57 * * #data_ptr the pointer points to gps data * #header the header for parsing GPGSV * #repeat_index the header may repeat for many lines * so the header index is for identifying repeated header * #token_index is the index of the parsing data separated by "," * the start is 1 * #result to store the result of the parser input * * #result bool - parsed successfully **/ bool parse_gnss_token(char *data_ptr, char *header, int repeat_index, int token_index, char *result) { bool gnss_parsed_result = FALSE; // To check GNSS data parsing is success bool on_header = FALSE; // For header int header_repeat_counter = 0; int header_char_index = 0; // each char in header index // For counting comma int counted_token_index = 0; // To hold the result character index bool data_found = FALSE; char *result_start = result; char header_found[10]; while (*data_ptr) { // 1. Packet start if (*data_ptr == GNSS_PACKET_START) { on_header = TRUE; header_char_index = 0; // to index each character in header data_found = FALSE; // is data part found data_ptr++; } // 2. For header parsing if (on_header) { if (*data_ptr == GNSS_TOKEN_SEPARATOR || header_char_index >= GNSS_HEADER_LENGTH) { on_header = FALSE; } else { header_found[header_char_index] = *data_ptr; if (header_char_index == GNSS_HEADER_LENGTH - 1) { // Now Header found header_found[header_char_index + 1] = '\0'; on_header = FALSE; if (!strcmp(header, header_found)) { // Some headers may repeat - to identify it set the repeat index if (header_repeat_counter == repeat_index) { //printf("Header: %s\r\n", header_found ); data_found = TRUE; } header_repeat_counter++; } } header_char_index++; } } // 3. data found if (data_found) { // To get the index data separated by comma if (counted_token_index == token_index && *data_ptr != GNSS_TOKEN_SEPARATOR) { // the data to parse *result++ = *data_ptr; gnss_parsed_result = TRUE; } if (*data_ptr == GNSS_TOKEN_SEPARATOR) { // if , counted_token_index++; // The comma counter for index } // Break if the counted_token_index(token_counter) greater than token_index(search_token) if (counted_token_index > token_index) { break; } } // Appending \0 to the end *result = '\0'; // To trim the data if ends with \r or \n str_trim(result_start); // Input data data_ptr++; } return gnss_parsed_result; } int main() { char res[100]; char *nem = "\ $GNRMC,080817.000,A,0852.089246,N,07636.289920,E,0.00,139.61,270322,,,A,V*04\r\n\\r\n\ $GNGGA,080817.000,0852.089246,N,07636.289920,E,1,5,1.41,11.246,M,-93.835,M,,*5E\r\n\ $GNVTG,139.61,T,,M,0.00,N,0.00,K,A*2F\r\n\ $GNGSA,A,3,30,19,17,14,13,,,,,,,,1.72,1.41,0.98,1*0A\r\n\ $GNGSA,A,3,,,,,,,,,,,,,1.72,1.41,0.98,3*02\r\n\ $GNGSA,A,3,,,,,,,,,,,,,1.72,1.41,0.98,6*07\r\n\ $GPGSV,3,1,12,06,64,177,,30,60,138,15,19,51,322,18,17,42,356,27,1*68\r\n\ $GPGSV,3,2,12,14,36,033,17,07,34,142,17,13,32,267,17,02,21,208,,1*6C\r\n\ $GPGSV,3,3,12,15,05,286,,01,05,037,,03,03,083,,20,02,208,,1*6B\r\n\ $GAGSV,1,1,00,7*73\r\n\ $GIGSV,1,1,00,1*7D\r\n\ $GNGLL,0852.089246,N,07636.289920,E,080817.000,A,A*43\r\n\ $PQTMANTENNASTATUS,1,0,1*4F\r\n"; printf("Parsing GNRMC\r\n"); printf("===============\r\n"); for(int i=1;i<=16;i++){ parse_gnss_token(nem, "GNRMC", 0, i, res); printf("Index: %d, Result: %s\r\n", i, res); } printf("Parsing GNVTG (First Parameter)\r\n"); printf("================================"); // GNVTG - Header, 0 - Repeat Index(if header is repeating), 1 - Value Index, parse_gnss_token(nem, "GNVTG", 0, 1, res); printf("\r\nGNVTG: %s\r\n", res); return 0; }