/* Program: decimate.c Revision: 22-Dec-2021 The Tektronics 3054 oscilloscope is used to write an ascii raw data file. This file has 10,000 lines of time, amplitude values separated by a comma. As written by the scope these raw data files have a .isf file name suffix. Typical lines in these files look like the following: ... -3.732e-03,-0.298 # -3.732 msec wrt trigger -298 mVolts -3.730e-03,-0.294 # -3.730 msec wrt trigger -294 mVolts ... 0.0e+00,0.006 # 0 sec wrt trigger 6 mVolts 2e-06,0.006 # 2 usec wrt trigger 6 mVolts ... 3.848e-03,0.316 # 3.848 msec wrt trigger 316 mVolts 3.850e-03,0.314 # 3.850 msec wrt trigger 314 mVolts ... There is nothing else in these files - they contain only the time offset from the trigger, amplitude pairs. There is a CRLF at the end of each line and an extra final CRLF at the end of the file. Notes: - The time offset from the trigger point is always written in an exponent format even for the value zero. - The amplitude information is always written in a floating point format except that I have also see single digits, e.g. 0, as the amplitude information. This program reads a .isf input file and writes an output file. - This programs reads in 10 lines of the input file at a time. - It averages these 10 amplitude values and then writes 1 line to the output file. - This 1 line written to the output file contains the time stamp from the middle line of the 10 input lines and the average amplitude value of the 10 input lines. The following is the general outline of this program: Open the input file for reading. Open the output file for writing. While the input file has un-read lines Read in 10 lines at a time Calculate the average amplitude Write 1 line output with: middle time, average amplitude Close all files. For reference this is the "printable" ASCII code table: Character Decimal Hex Character Decimal Hex --------- ------- --- --------- ------- --- line feed = 10 0a : = 58 3a form feed = 12 0c ; = 59 3b return = 13 0d < = 60 3c space = 32 20 = = 61 3d > = 62 3e ! = 33 21 ? = 63 3f " = 34 22 @ = 64 40 # = 35 23 $ = 36 24 A:Z = 65:90 41:5a % = 37 25 & = 38 26 [ = 91 5b ' = 39 27 \ = 92 5c ] = 93 5d ( = 40 28 ^ = 94 5e ) = 41 29 _ = 95 5f * = 42 2a ` = 96 60 + = 43 2b , = 44 2c a:z = 97:122 61:7a - = 45 2d . = 46 2e { = 123 7b / = 47 2f | = 124 7c } = 125 7d 0:9 = 48:57 30:39 ~ = 126 7e */ #include #define MAX_BUFF 100 #define MAX_LINE 80 #define MAX_ARRAY 10020 #define MAX_FILTERED_ARRAY 1020 /** Declare the functions aka provide the function prototypes **/ /** with dummy arguments **/ void exit ( int location ) ; void clearbuf ( char working_line_buffer[] ) ; void parse_t_a ( char input__buffer[], char time_buffer[], char ampt_buffer[] ) ; main () { char filename[MAX_LINE] ; char work_buf[MAX_BUFF] ; char time_buf[MAX_BUFF] ; char ampt_buf[MAX_BUFF] ; FILE *fp_input_file ; FILE *fp_check_file ; FILE *fp_filtered_data ; FILE *fp_velocity_data ; int parse_count = 0 ; int filter_loop_cnt = 0 ; int filter_internal_cnt = 0 ; int filter_ampt_index = 0 ; double this_time ; double this_ampt ; double avg_ampt ; double time_array[MAX_ARRAY] ; double ampt_array[MAX_ARRAY] ; double filtered_time[MAX_FILTERED_ARRAY] ; double filtered_ampt[MAX_FILTERED_ARRAY] ; /** Get the filenames of the: input .isf file from the scope, **/ /** the Parser Check output file, the Filter output file, and **/ /** the Velocity output file. **/ printf("Enter the filename of the .isf file to READ: "); gets(filename); if ((fp_input_file = fopen(filename, "r")) == NULL) { printf("\nERROR: Can't open input .isf file %s\n", filename); exit (1); } printf("Enter the filename to receive the PARSE CHECK: "); gets(filename); if ((fp_check_file = fopen(filename, "w")) == NULL) { printf("\nERROR: Can't open output check file %s\n", filename); exit (2); } printf("Enter the filename to receive the FILTERED data: "); gets(filename); if ((fp_filtered_data = fopen(filename, "w")) == NULL) { printf("\nERROR: Can't open filtered output file %s\n", filename); exit (3); } printf("Enter the filename to receive the VELOCITY data: "); gets(filename); if ((fp_velocity_data = fopen(filename, "w")) == NULL) { printf("\nERROR: Can't open velocity output file %s\n", filename); exit (4); } /** We now have all the information that we need to start the actual work **/ /** One at a time, read a whole line into the workbuf and then call **/ /** the parse_time_amplitude function. This function puts the TIME **/ /** and AMPLITUDE information into separate character string arrays so **/ /** that the sscanf funtion can be used to extract this information **/ /** into normal double floating point variables. **/ parse_count = 0 ; clearbuf( work_buf ) ; fgets( work_buf, MAX_LINE, fp_input_file ); while( work_buf[0] != '\0' && work_buf[0] != '\r' ) /** note \r **/ { parse_t_a( work_buf, time_buf, ampt_buf ) ; /** fputs( outputbuf, fp_check_file ); **/ sscanf( time_buf, "%le", &this_time ); /** printf( "\nThe time is: %12.7f", this_time ); **/ fprintf( fp_check_file, "%12.7f ", this_time ); sscanf( ampt_buf, "%lf", &this_ampt ); fprintf( fp_check_file, "%12.7f\n", this_ampt ); time_array[parse_count] = this_time ; ampt_array[parse_count] = this_ampt ; ++parse_count ; clearbuf( work_buf ); fgets( work_buf, MAX_LINE, fp_input_file ); } fclose( fp_input_file ) ; fclose( fp_check_file ) ; /** The TIME and AMPLITUDE data is now in separate arrays of **/ /** type double so we can work with it. **/ /** **/ /** The first filter is just a decimation by a factor of ten. **/ /** For each group of ten input values: pick the middle TIME **/ /** stamp and average the ten AMPLITUDE values. Write the **/ /** resulting TIME, AMPLITUDE pair into the filtered output **/ /** file as floating point decimal numbers. **/ /** **/ /** For the 10,000 input pairs in the .isf file from the Tek **/ /** TDS 3054 scope this should give us 1,000 TIME, AMPLITUDE **/ /** pairs in the filtered output file. **/ /** **/ /** Also write these 1000 TIME & AMPLIDUTE values into a pair **/ /** of arrays of type double called filtered_time and **/ /** filtered_ampt. **/ filter_loop_cnt = 0 ; filter_internal_cnt = 0 ; while( filter_loop_cnt < 1000 ) { filter_internal_cnt = 0 ; avg_ampt = 0.0 ; while( filter_internal_cnt < 10 ) { filter_ampt_index = (10 * filter_loop_cnt) + filter_internal_cnt ; avg_ampt = avg_ampt + ampt_array[filter_ampt_index] ; ++filter_internal_cnt ; } this_time = time_array[ (10 * filter_loop_cnt) + 5] ; avg_ampt = avg_ampt / 10.0 ; filtered_time[filter_loop_cnt] = this_time ; filtered_ampt[filter_loop_cnt] = avg_ampt ; fprintf( fp_filtered_data, "%12.7f ", this_time ); fprintf( fp_filtered_data, "%12.7f\n", avg_ampt ); ++filter_loop_cnt ; } fclose( fp_filtered_data ) ; /** Now start the steps to calculate Velocity and Position. **/ /** **/ /** First remove and DC offset in the Amplidute data. Do this **/ /** by calculating the average value of about the first third **/ /** of the 1000 entry filtered_ampt array. Here we assume **/ /** that the trigger point on the scope will be set for about **/ /** 40% so that all of the Amplitude data in the first about **/ /** one third of this array will just be baseline values. **/ /** **/ /** For now I will skip the first 10 entries (0:9) and **/ /** calculate the average DC baseline based on the filtered_ampt **/ /** array entries 11:300 (10:299) i.e. 290 entries. **/ /** **/ #define START_INDEX_DC_BASELINE 10 /** Array indexes **/ #define LAST_INDEX_DC_BASELINE 299 /** start at zero **/ avg_ampt = 0.0 ; filter_loop_cnt = START_INDEX_DC_BASELINE ; while( filter_loop_cnt <= LAST_INDEX_DC_BASELINE ) { avg_ampt = avg_ampt + filtered_ampt[filter_loop_cnt] ; ++filter_loop_cnt ; } avg_ampt = avg_ampt / (( LAST_INDEX_DC_BASELINE - START_INDEX_DC_BASELINE) + 1) ; /** avg_ampt is now the DC Baseline of array filtered_ampt **/ /** based on entries START_INDEX_DC_BASELINE through **/ /** START_INDEX_DC_BASELINE **/ /** **/ /** Now subtract the DC Baseline from each entry in **/ /** the array filtered_ampt[] **/ filter_loop_cnt = 0 ; while( filter_loop_cnt < 1000 ) { filtered_ampt[filter_loop_cnt] = filtered_ampt[filter_loop_cnt] - avg_ampt ; ++filter_loop_cnt ; } filter_loop_cnt = 0 ; while( filter_loop_cnt < 1000 ) { fprintf( fp_velocity_data, "%12.7f ", filtered_time[filter_loop_cnt] ); fprintf( fp_velocity_data, "%12.7f\n", filtered_ampt[filter_loop_cnt] ); ++filter_loop_cnt ; } fclose( fp_velocity_data ) ; return ; } /* This is the CLEARBUF function. It is used to set all elements of the workbuf to '\0'. Note that because the arguments passed to this function are the names of two arrays, this function works on the actual arrays, not on temporary local copies of the arrays */ void clearbuf( char linebuf[] ) /* function definition */ { int local_index = 0; while( local_index < MAX_LINE ) { linebuf[local_index] = '\0'; ++local_index; } return; } /* This is the PARSE_TIME_AMPLITUDE function. For the TIME information this routine just pulls characters one at a time from the in_line[] buffer until it finds the "," comma character OR reaches the the end of line \0 null. It will only reach the end of line null if there is an error in the .isf file. It puts all of the characters up to but not including the "," comma character into the time_out[] buffer. It then appends a \0 null to the end of the time_out[] buffer so that this buffer is ready to be processed as a character string array. Next it skips over the "," comma and then puts out all of the remaining characters from the in_line[] buff up to but not including the terminating \0 null and puts all of these characters into the ampt_out[] buffer. Finally it appends a \0 null to the end of the ampt_out[] buffer so that this buffer is ready to be processed as a character string array. Function Definition of parse_t_a */ void parse_t_a ( char in_line[], char time_out[], char ampt_out[] ) { int read_index = 0 ; int write_index = 0 ; /** Extract the TIME information and make it a string **/ while( in_line[read_index] != '\0' && in_line[read_index] != ',' ) { time_out[write_index] = in_line[read_index] ; ++read_index ; ++write_index ; } time_out[write_index] = '\0'; /** could also be \n **/ time_out[write_index + 1] = '\0'; /** Extract the AMPLITUDE information and make it a string **/ ++read_index ; /** skip the comma **/ write_index = 0 ; /** reset the write_index **/ while( in_line[read_index] != '\0' ) { ampt_out[write_index] = in_line[read_index] ; ++read_index ; ++write_index ; } ampt_out[read_index] = '\0'; /** could also be \n **/ ampt_out[read_index + 1] = '\0'; return; }