/* ************************************************************** */ /* * Module VTCOIL * This module contains functions to make integrated voltage * measurements from a rotating coil. * * Zachary Wolf * 9/9/98 */ /* ************************************************************** */ /* INCLUDES */ #include #include #include #include #include #include "vtcoil.h" #include "vtcoilui.h" #include "k7011.h" #include "cm2100.h" #include "pdi5025.h" /* ************************************************************** */ /* PRIVATE PARAMETERS */ static char log_file[100]; static struct vtcoil_param_struct vtcoil_param; /* ************************************************************** */ /* PRIVATE GLOBAL VARIABLES */ static int k7011_ID; static int cm2100_ID; static int pdi5025_ID; static char msg[80]; /* ************************************************************** */ /* PRIVATE FUNCTIONS */ int vtcoil_check_param(void); void vtcoil_error(char* message); void vtcoil_message(char* message); char vtcoil_check_vtsamp(int num_samp, double vt[]); void vtcoil_correct_offset(int num_samp, double vt[]); void vtcoil_calc_vtfft(int num_samp, double vt[], double vtfft_re[], double vtfft_im[]); void vtcoil_calc_vthar(int num_samp, double vtfft_re[], double vtfft_im[], int num_rev, int num_har, double vthar_re[], double vthar_im[]); char vtcoil_check_vthar(int num_samp, double vtfft_re[], double vtfft_im[], int num_rev, int num_har); void vtcoil_log_vthar(char rot_dir, int num_rev, int num_har, double vthar_re[], double vthar_im[]); void vtcoil_log_vthar_ave(int num_har, double vthar_re_ave[], double vthar_re_rms[], double vthar_im_ave[], double vthar_im_rms[]); void vtcoil_log_message(char* message); /* ************************************************************** */ /* PUBLIC FUNCTIONS */ /* ************************************************************** */ /* * vtcoil_init * This function is used to initialize the integrated voltage * measurement system. * * Zachary Wolf * 9/9/98 */ void vtcoil_init(char log_file_in[], struct vtcoil_param_struct vtcoil_par) { /* Declare variables */ int err; /* Save parameters for future use */ strcpy(log_file, log_file_in); vtcoil_param = vtcoil_par; /* Check all parameter values to find any problems */ err = vtcoil_check_param(); if (err != 0) { vtcoil_error("Problem with parameter values"); return; } /* Initialize the required hardware */ if (vtcoil_param.config == VTCOIL_CM2100_PDI5025) { cm2100_init(vtcoil_param.board_addr, vtcoil_param.cm2100_addr, &cm2100_ID); pdi5025_init(vtcoil_param.board_addr, vtcoil_param.pdi5025_addr, &pdi5025_ID); pdi5025_set_rotary_encoder_trigger(pdi5025_ID, vtcoil_param.num_encoder_pulse_per_rev); } else if (vtcoil_param.config == VTCOIL_K7011_CM2100_PDI5025) { k7011_init(vtcoil_param.board_addr, vtcoil_param.k7011_addr, &k7011_ID); cm2100_init(vtcoil_param.board_addr, vtcoil_param.cm2100_addr, &cm2100_ID); pdi5025_init(vtcoil_param.board_addr, vtcoil_param.pdi5025_addr, &pdi5025_ID); pdi5025_set_rotary_encoder_trigger(pdi5025_ID, vtcoil_param.num_encoder_pulse_per_rev); } else { vtcoil_error("Unknown integrated voltage system configuration"); return; } /* Done */ return; } /* ************************************************************** */ /* * vtcoil_locate_index * This function is used to locate the index pulse of the encoder * connected to the PDI5025 integrator. * * Zachary Wolf * 9/10/98 */ void vtcoil_locate_index(void) { /* Declare variables */ int err; double acc, vel, num_rev; int index; /* Check the configuration */ if (vtcoil_param.config != VTCOIL_CM2100_PDI5025 && vtcoil_param.config != VTCOIL_K7011_CM2100_PDI5025) { vtcoil_error("vtcoil_locate_index: wrong configuration"); return; } /* Check all parameter values */ err = vtcoil_check_param(); if (err != 0) { vtcoil_error("Problem with parameter values"); return; } /* Message */ vtcoil_message(""); vtcoil_message("Finding the encoder index pulse..."); /* Turn the motor 3 revolutions */ acc = vtcoil_param.motor_acc; vel = vtcoil_param.motor_vel; num_rev = vtcoil_param.rel_motor_rot_dir * 3.; cm2100_start_rel_move(cm2100_ID, acc, vel, num_rev); /* While the motor is turning, locate the index pulse */ index = pdi5025_locate_index(pdi5025_ID); /* Conclude the motor rotation */ cm2100_end_rel_move(cm2100_ID, num_rev); /* Make sure the index pulse was found */ if (index != 1) vtcoil_error("vtcoil_locate_index: index not found"); /* Undo the motor turns in case a slip ring is not used */ cm2100_rel_move(cm2100_ID, acc, vel, -num_rev); /* Done */ return; } /* ************************************************************** */ /* * vtcoil_route_signal * This function is used to select a multiplexer channel and the * integrator channel to route the signal to the integrator. * * Input: * mux_chan, multiplexer channel (0 to open all channels) * integ_chan, integrator channel (pdi5025 channel 'A' or 'B') * * Zachary Wolf * 9/10/98 */ void vtcoil_route_signal(int mux_chan, char integ_chan) { /* Declare variables */ int err; /* Check the input parameters */ if (integ_chan != 'A' && integ_chan != 'B') { vtcoil_error("vtcoil_route_signal: unknown channel"); return; } if (mux_chan < 0 || mux_chan > 40) { vtcoil_error("vtcoil_route_signal: unknown channel"); return; } /* Check the configuration */ if (vtcoil_param.config != VTCOIL_CM2100_PDI5025 && vtcoil_param.config != VTCOIL_K7011_CM2100_PDI5025) { vtcoil_error("vtcoil_route_signal: wrong configuration"); return; } /* Save the integrator channel for measurement functions */ pdi5025_chan = integ_chan; /* If there is a multiplexer... */ if (vtcoil_param.config == VTCOIL_K7011_CM2100_PDI5025) { /* Open all multiplexer channels so two channels can not be simultaneously closed */ k7011_open_all(k7011_ID); /* Close the desired channel */ if (mux_chan > 0) k7011_close_card_chan(k7011_ID, 1, mux_chan); } /* Done */ return; } /* ************************************************************** */ /* * vtcoil_set_gain * This function is used to set the gain of the Metrolab integrator. * * Input: * gain, integrator gain, 1, 2, 5, 10, 20, 50, 100, 200, 500, 1000 * * Zachary Wolf * 8/24/99 */ void vtcoil_set_gain(int gain) { /* Check the configuration */ if (vtcoil_param.config != VTCOIL_CM2100_PDI5025 && vtcoil_param.config != VTCOIL_K7011_CM2100_PDI5025) { vtcoil_error("vtcoil_set_gain: wrong configuration"); return; } /* Check input parameters */ if (gain != 1 && gain != 2 && gain != 5 && gain != 10 && gain != 20 && gain != 50 && gain != 100 && gain != 200 && gain != 500 && gain != 1000) { Fmt(msg, "%s max_volt) max_volt = fabs(v[i]); } /* Determine the maximum allowed gain */ /* Keep max_volt * gain < Vmax */ Vmax = 3.5; gain = 1; if (max_volt * 2. < Vmax) gain = 2; if (max_volt * 5. < Vmax) gain = 5; if (max_volt * 10. < Vmax) gain = 10; if (max_volt * 20. < Vmax) gain = 20; if (max_volt * 50. < Vmax) gain = 50; if (max_volt * 100. < Vmax) gain = 100; if (max_volt * 200. < Vmax) gain = 200; if (max_volt * 500. < Vmax) gain = 500; if (max_volt * 1000. < Vmax) gain = 1000; /* Set a practical limit based on noise levels, if needed */ /* if (gain > 100) gain = 100; */ /* Set the integrator gain */ pdi5025_set_gain(pdi5025_ID, pdi5025_chan, gain); /* Save the gain for future measurements */ pdi5025_gain = gain; /* Undo the motor turns in case a slip ring is not used */ cm2100_rel_move(cm2100_ID, acc, vel, -num_rev); /* Done */ return; } /* ************************************************************** */ /* * vtcoil_get_vt * This function measures the integrated voltage from a coil as a * function of angle. The voltage signal must have already been * routed to the integrator. The integrator gain must already be * set, eg. by a call to vtcoil_auto_gain. * * Input: * rot_dir, encoder rotation direction, '+' = CCW or '-' = CW * num_samp, number of integrated voltage samples to take * * Output: * vt[], the integrated voltage measurements, vt[0] = 0, vt[i] = samples (i = 1 to num_samp) * * Zachary Wolf * 9/18/98 */ void vtcoil_get_vt(char rot_dir, int num_samp, double vt[]) { /* Declare variables */ int err; double acc, vel, num_rev; int gain; int num_samp_per_rev; char data_ok; double* rev_vt; int i; int rp_count; /* Check the input parameters */ if (rot_dir != '+' && rot_dir != '-') { vtcoil_error("vtcoil_get_vt: unknown rotation direction"); return; } if (num_samp < 1 || num_samp > 5000) { vtcoil_error("vtcoil_get_vt: improper num_samp"); return; } if (num_samp % vtcoil_param.num_samp_per_rev != 0) { vtcoil_error("vtcoil_get_vt: must sample over a complete number of rotations"); return; } /* Check the configuration */ if (vtcoil_param.config != VTCOIL_CM2100_PDI5025 && vtcoil_param.config != VTCOIL_K7011_CM2100_PDI5025) { vtcoil_error("vtcoil_auto_gain: wrong configuration"); return; } /* Check all parameter values */ err = vtcoil_check_param(); if (err != 0) { vtcoil_error("Problem with parameter values"); return; } /* Turn the motor 5 revolutions in addition to the sampled revolutions */ rp_count = 0; remeasure: acc = vtcoil_param.motor_acc; vel = vtcoil_param.motor_vel; num_rev = vtcoil_param.rel_motor_rot_dir * (num_samp / vtcoil_param.num_samp_per_rev + 5); if (rot_dir == '-') num_rev = -num_rev; cm2100_start_rel_move(cm2100_ID, acc, vel, num_rev); /* Wait until the motor is up to speed, 2 revolutions */ Delay(2. / vel); /* While the motor is turning, sample the integrated voltage */ pdi5025_get_VT_rotary_encoder(pdi5025_ID, pdi5025_chan, pdi5025_gain, rot_dir, vtcoil_param.num_samp_per_rev, num_samp, vt); /* Conclude the motor rotation */ cm2100_end_rel_move(cm2100_ID, num_rev); /* Remeasure if there are bad data points */ /* Undo the motor turns first */ data_ok = vtcoil_check_vtsamp(num_samp, vt); if (data_ok == 'n' && rp_count < 4) { rp_count++; printf(" Rp"); cm2100_rel_move(cm2100_ID, acc, vel, -num_rev); goto remeasure; } /* Correct the data for integrator drift */ vtcoil_correct_offset(num_samp, vt); /* Reverse the vt array if the rotation direction is negative */ if (rot_dir == '-') { rev_vt = (double*) malloc((num_samp + 1) * sizeof(double)); for (i = 0; i <= num_samp; i++) rev_vt[i] = vt[num_samp - i]; for (i = 0; i <= num_samp; i++) vt[i] = rev_vt[i]; free(rev_vt); } /* Done */ return; } /* ************************************************************** */ /* * vtcoil_get_vthar * This function measures the integrated voltage from a coil as a * function of angle. The voltage signal must have already been * routed to the integrator. The integrator gain must already be * set, eg. by a call to vtcoil_auto_gain. * It then finds the integrated voltage signals at 1, 2, 3, ... cycles * per revolution. * * Input: * rot_dir, encoder rotation direction, '+' = CCW or '-' = CW * num_rev, number of revolutions to measure over * num_har, number of harmonics to record * * Output: * vthar_re[0 to num_har], vt harmonics, real part, [0] is unused * vthar_im[0 to num_har], vt harmonics, imaginary part, [0] is unused * * Zachary Wolf * 10/6/98 */ void vtcoil_get_vthar(char rot_dir, int num_rev, int num_har, double vthar_re[], double vthar_im[]) { /* Declare variables */ int num_samp; double* vt; double* vtfft_re; double* vtfft_im; /* Check input parameters */ if (rot_dir != '+' && rot_dir != '-') { vtcoil_error("vtcoil_get_vthar: unknown rotation direction"); return; } if (num_rev < 1 || num_rev * vtcoil_param.num_samp_per_rev > 5000) { vtcoil_error("vtcoil_get_vthar: improper num_rev"); return; } if (num_har > vtcoil_param.num_samp_per_rev / 2) { vtcoil_error("vtcoil_get_vthar: improper num_har"); return; } /* Determine the number of vt samples to take */ num_samp = num_rev * vtcoil_param.num_samp_per_rev; /* Allocate space for the vt samples */ vt = (double*) malloc((num_samp + 1) * sizeof(double)); /* Get the vt samples */ vtcoil_get_vt(rot_dir, num_samp, vt); /* Allocate space for the FFT data */ vtfft_re = (double*) malloc(num_samp * sizeof(double)); vtfft_im = (double*) malloc(num_samp * sizeof(double)); /* Take the FFT of the vt samples */ vtcoil_calc_vtfft(num_samp, vt, vtfft_re, vtfft_im); /* Extract the harmonics */ vtcoil_calc_vthar(num_samp, vtfft_re, vtfft_im, num_rev, num_har, vthar_re, vthar_im); /* Update the user interface */ if (vtcoil_param.show_ui == VTCOIL_TRUE) vtcoilui_update(num_samp, vt, vtfft_re, vtfft_im); /* Log the result */ vtcoil_log_vthar(rot_dir, num_rev, num_har, vthar_re, vthar_im); /* Record the data as a diagnostic if it is noisy */ vtcoil_check_vthar(num_samp, vtfft_re, vtfft_im, num_rev, vtcoil_param.num_samp_per_rev / 2); /* Free temporary storage space */ free(vt); free(vtfft_re); free(vtfft_im); /* Done */ return; } /* ************************************************************** */ /* * vtcoil_get_vthar_pmave * This function measures the integrated voltage harmonics first for * coil roatation in the + direction, then for coil rotation in the - * direction. It then averages the two measurements and returns * the results. * The voltage signal must have already been * routed to the integrator. The integrator gain must already be * set, eg. by a call to vtcoil_auto_gain. * * Input: * num_rev, number of revolutions to measure over * num_har, number of harmonics to record * * Output: * vthar_re[0 to num_har], vt harmonics, real part, [0] is unused * vthar_im[0 to num_har], vt harmonics, imaginary part, [0] is unused * * Zachary Wolf * 7/7/99 */ void vtcoil_get_vthar_pmave(int num_rev, int num_har, double vthar_re[], double vthar_im[]) { /* Declare variables */ int err; double* vthar_re_pos; double* vthar_im_pos; double* vthar_re_neg; double* vthar_im_neg; int i; /* Check input parameters */ if (num_rev < 1 || num_rev * vtcoil_param.num_samp_per_rev > 5000) { vtcoil_error("vtcoil_get_vthar_pmave: improper num_rev"); return; } if (num_har > vtcoil_param.num_samp_per_rev / 2) { vtcoil_error("vtcoil_get_vthar_pmave: improper num_har"); return; } /* Allocate space for the intermediate VT harmonics arrays */ vthar_re_pos = (double*) malloc((num_har + 1) * sizeof(double)); vthar_im_pos = (double*) malloc((num_har + 1) * sizeof(double)); vthar_re_neg = (double*) malloc((num_har + 1) * sizeof(double)); vthar_im_neg = (double*) malloc((num_har + 1) * sizeof(double)); if (vthar_re_pos == NULL || vthar_im_pos == NULL) vtcoil_error("vtcoil_get_vthar_pmave: Problem allocating VT arrays, must exit"); if (vthar_re_neg == NULL || vthar_im_neg == NULL) vtcoil_error("vtcoil_get_vthar_pmave: Problem allocating VT arrays, must exit"); /* Perform a measurement of the integrated voltage harmonics using + coil rotation */ vtcoil_get_vthar('+', num_rev, num_har, vthar_re_pos, vthar_im_pos); /* Perform a measurement using - coil rotation */ vtcoil_get_vthar('-', num_rev, num_har, vthar_re_neg, vthar_im_neg); /* Average the + and - rotation results */ for (i = 1; i <= num_har; i++) { vthar_re[i] = (vthar_re_pos[i] + vthar_re_neg[i]) / 2.; vthar_im[i] = (vthar_im_pos[i] + vthar_im_neg[i]) / 2.; } /* Free temporary storage */ free(vthar_re_pos); free(vthar_im_pos); free(vthar_re_neg); free(vthar_im_neg); /* Done */ return; } /* ************************************************************** */ /* * vtcoil_get_vthar_ave * This function measures the integrated voltage harmonics from a measurement * coil several times. The signal must have already been routed to * the integrator. The gain of the integrator must have already been set. * This function then computes the average and rms variation of the measurements. * * Input: * num_meas, number of measurements for averaging * num_rev, number of revolutions to measure over * num_har, number of harmonics to record * * Output: * vthar_re_ave[0 to num_har], vt harmonics (Vs), real part, average, [0] is unused * vthar_re_rms[0 to num_har], vt harmonics (Vs), real part, rms variation, [0] is unused * vthar_im_ave[0 to num_har], vt harmonics (Vs), imaginary part, average, [0] is unused * vthar_im_rms[0 to num_har], vt harmonics (Vs), imaginary part, rms variation, [0] is unused * * Zachary Wolf * 7/7/99 */ void vtcoil_get_vthar_ave(int num_meas, int num_rev, int num_har, double vthar_re_ave[], double vthar_re_rms[], double vthar_im_ave[], double vthar_im_rms[]) { /* Declare variables */ int err; int i, j; #define MAX_NUM_HAR 50 #define MAX_NUM_MEAS 50 double vthar_re[MAX_NUM_HAR + 1]; double vthar_im[MAX_NUM_HAR + 1]; double vthar_meas_re[MAX_NUM_HAR + 1][MAX_NUM_MEAS + 1]; double vthar_meas_im[MAX_NUM_HAR + 1][MAX_NUM_MEAS + 1]; double meas_values[MAX_NUM_MEAS]; /* Check input parameters */ if (num_meas < 1 || num_meas > MAX_NUM_MEAS) { vtcoil_error("vtcoil_get_vthar_ave: improper num_meas"); return; } if (num_rev < 1 || num_rev * vtcoil_param.num_samp_per_rev > 5000) { vtcoil_error("vtcoil_get_vthar_ave: improper num_rev"); return; } if (num_har > vtcoil_param.num_samp_per_rev / 2 || num_har > MAX_NUM_HAR) { vtcoil_error("vtcoil_get_vthar_ave: improper num_har"); return; } /* Message */ printf("\nPerforming measurement #"); /* Loop over the measurements, save the results */ for (j = 1; j <= num_meas; j++) { printf("%2i", j); vtcoil_get_vthar_pmave(num_rev, num_har, vthar_re, vthar_im); for (i = 1; i <= num_har; i++) { vthar_meas_re[i][j] = vthar_re[i]; vthar_meas_im[i][j] = vthar_im[i]; } } /* End messages */ printf("\n"); /* Calculate the average and rms variation of the measurements for each harmonic */ for (i = 1; i <= num_har; i++) { for (j = 1; j <= num_meas; j++) meas_values[j-1] = vthar_meas_re[i][j]; StdDev(meas_values, num_meas, &vthar_re_ave[i], &vthar_re_rms[i]); for (j = 1; j <= num_meas; j++) meas_values[j-1] = vthar_meas_im[i][j]; StdDev(meas_values, num_meas, &vthar_im_ave[i], &vthar_im_rms[i]); } /* Log the result */ vtcoil_log_vthar_ave(num_har, vthar_re_ave, vthar_re_rms, vthar_im_ave, vthar_im_rms); /* Done */ return; } /* ************************************************************** */ /* * vtcoil_protect * This function is used to protect the integrated voltage system * from overvoltages due to ramping a magnet, etc. * * Zachary Wolf * 8/24/99 */ void vtcoil_protect(void) { /* Set the integrator gain to 1 */ if (vtcoil_param.config == VTCOIL_CM2100_PDI5025 || vtcoil_param.config == VTCOIL_K7011_CM2100_PDI5025) pdi5025_set_gain(pdi5025_ID, pdi5025_chan, 1); /* If using a multiplexer, open all channels */ if (vtcoil_param.config == VTCOIL_K7011_CM2100_PDI5025) k7011_open_all(k7011_ID); /* Done */ return; } /* ************************************************************** */ /* * vtcoil_exit * This function is used to exit the integrated voltage system. * * Zachary Wolf * 9/15/98 */ void vtcoil_exit(void) { /* Set the integrator gain to 1 */ pdi5025_set_gain(pdi5025_ID, pdi5025_chan, 1); /* If using a multiplexer, open all channels */ if (vtcoil_param.config == VTCOIL_K7011_CM2100_PDI5025) k7011_open_all(k7011_ID); /* Close all devices */ if (vtcoil_param.config == VTCOIL_CM2100_PDI5025) { cm2100_exit(cm2100_ID); pdi5025_exit(pdi5025_ID); } else if (vtcoil_param.config == VTCOIL_K7011_CM2100_PDI5025) { k7011_exit(k7011_ID); cm2100_exit(cm2100_ID); pdi5025_exit(pdi5025_ID); } else { vtcoil_error("Unknown VT configuration"); return; } /* Done */ return; } /* ************************************************************** */ /* INTERNAL FUNCTIONS */ /* ************************************************************** */ /* * vtcoil_error * This function handles errors for the IMAG module. * * Input: * message, string to display in standard I/O * * Zachary Wolf * 10/11/98 */ void vtcoil_error(char* message) { /* Declare variables */ char buf[80]; /* Notify the operator of the error */ printf("\nVTCOIL ERROR: %s\n", message); Beep(); Delay(.5); Beep(); /* Terminate the program if the operator desires */ printf("Press ENTER to continue.\nPress any key then ENTER to terminate program.\n"); fgets(buf, 80, stdin); if (buf[0] == '\n') return; else exit(0); } /* ************************************************************** */ /* * vtcoil_message * This function handles messages for the IMAG module. * * Input: * message, string to display in standard I/O * * Zachary Wolf * 10/12/98 */ void vtcoil_message(char* message) { /* Print the message */ printf("%s\n", message); /* Done */ return; } /* ************************************************************** */ /* * vtcoil_check_param * This function checks that all required parameters have been assigned * appropriate values. * * Output: * err, 0 = no error, non-zero = error * * Zachary Wolf * 8/11/98 */ int vtcoil_check_param() { /* log_file */ if (CompareStrings(log_file, 0, "", 0, 0) == 0) { vtcoil_error("Log file not defined."); return -1; } /* vtcoil_param.board_addr */ if (vtcoil_param.board_addr < 0 || vtcoil_param.board_addr > 10) { Fmt(msg, "%s 30) { Fmt(msg, "%s 30) { Fmt(msg, "%s 30) { Fmt(msg, "%s 3000) { Fmt(msg, "%s vtcoil_param.num_encoder_pulse_per_rev) { Fmt(msg, "%s 100.) { Fmt(msg, "%s 20.) { Fmt(msg, "%s 1000) { Fmt(msg, "%s= 1.e-10) all_zero = 'n'; if (all_zero == 'y') { ok = 'n'; vtcoil_log_message("VTcoil problem: All vt[i] = 0, repeat the measurement"); } /* Find the maximum value to set a scale for fit goodness */ vt_max = 0.; for (i = 0; i <= num_samp; i++) if (fabs(vt[i]) > vt_max) vt_max = fabs(vt[i]); /* Compare each value to an expected value */ /* The expected value uses only previous samples to help find steps */ /* Don't use sample number 0 */ for (i = 3; i <= num_samp; i++) { vt_expect = vt[i-1] + (vt[i-1] - vt[i-2]); if (fabs(vt[i] - vt_expect) > (.3 * vt_max)) ok = 'n'; if (ok == 'n') break; } /* Write the samples to a file if ok = 'n' */ if (ok == 'n') { file_ptr = fopen("vt_check_samp.dat", "w"); if (file_ptr == NULL) vtcoil_error("Problem opening vt_data_check.dat"); for (i = 0; i <= num_samp; i++) fprintf(file_ptr, "%f\n", vt[i]); fclose(file_ptr); } /* Done */ return ok; } /* ************************************************************** */ /* * vtcoil_correct_offset * This function corrects integrated voltage samples for integrator * offset. The offset voltage produces a linear ramp. The ramp is * calculated and subtracted from the samples. * * Input: * num_samp, number of samples * vt[0 to num_samp], integrated voltage samples * * Zachary Wolf * 9/20/98 */ void vtcoil_correct_offset(int num_samp, double vt[]) { /* Declare variables */ double* ramp; int i; /* Create an array to hold the ramp values */ ramp = (double*) malloc((num_samp + 1) * sizeof(double)); if (ramp == NULL) { vtcoil_error("correct_offset: unable to set up ramp array"); return; } /* Compute the ramp using the 0'th and n'th samples (ideally are the same) */ for (i = 0; i <= num_samp; i++) ramp[i] = (vt[num_samp] - vt[0]) * i / num_samp; /* Subtract the ramp from the measured samples */ for (i = 0; i <= num_samp; i++) vt[i] = vt[i] - ramp[i]; /* Free the ramp array */ free(ramp); /* Done */ return; } /* ************************************************************** */ /* * vtcoil_calc_vtfft * This function takes the integrated voltage samples and computes * the FFT of the samples. For an input signal of the form A * cos(nwt + phi), * the function returns A * e^(j phi). * * Input: * num_samp, number of integrated voltage samples * vt[0 to num_samp], the integrated voltage measurements, vt[num_samp] is not used * * Output: * vtfft_re[0 to num_samp - 1], real part of FFT coeficients * vtfft_im[0 to num_samp - 1], imaginary part of FFT coeficients * * Zachary Wolf * 9/26/98 */ void vtcoil_calc_vtfft(int num_samp, double vt[], double vtfft_re[], double vtfft_im[]) { /* Declare variables */ int i; /* Check the input parameters */ if ( num_samp != 2 && num_samp != 4 && num_samp != 8 && num_samp != 16 && num_samp != 32 && num_samp != 64 && num_samp != 128 && num_samp != 256 && num_samp != 512 && num_samp != 1024 && num_samp != 2048 && num_samp != 4096) { vtcoil_error("vtcoil_calc_vtfft: improper num_samp"); return; } /* Initialize the intermediate arrays */ for (i = 0; i < num_samp; i++) vtfft_re[i] = vt[i]; /* Calculate the FFT */ ReFFT(vtfft_re, vtfft_im, num_samp); /* Take out the N/2 factor, this leaves signal amplitudes in Vs */ /* The N is from the definition of the FFT in ReFFT */ /* The 2 comes from the cosine, taking it out gives the signal amplitude */ for (i = 0; i < num_samp; i++) { vtfft_re[i] = 2 * vtfft_re[i] / num_samp; vtfft_im[i] = 2 * vtfft_im[i] / num_samp; } /* Done */ return; } /* ************************************************************** */ /* * vtcoil_calc_vthar * This function uses the FFT of the integrated voltage samples * as input. The FFT consists of 1 cycle in the FFT record, 2 cycles, ... * The dipole signal has num_rev cycles in the FFT record, * the quadrupole has 2 * num_rev, etc. The function * extracts the signals at multiples of the number of coil revolutions. * * Input: * num_samp, number of integrated voltage samples * vtfft_re[0 to num_samp - 1], real part of FFT coeficients * vtfft_im[0 to num_samp - 1], imaginary part of FFT coeficients * num_rev, number of coil revolutions in the samples * num_har, number of harmonics desired * * Output: * vthar_re[0 to num_har], real part of vt harmonics * vthar_im[0 to num_har], imaginary part of vt harmonics * * Zachary Wolf * 10/5/98 */ void vtcoil_calc_vthar(int num_samp, double vtfft_re[], double vtfft_im[], int num_rev, int num_har, double vthar_re[], double vthar_im[]) { /* Declare variables */ int i; /* Check the input parameters */ if (num_har * num_rev > num_samp / 2) { vtcoil_error("vtcoil_calc_vthar: improper num_har"); return; } /* The harmonics are at 1, 2, 3, ... cycles per revolution */ vthar_re[0] = 0.; vthar_im[0] = 0.; for (i = 1; i <= num_har; i++) { vthar_re[i] = vtfft_re[i * num_rev]; vthar_im[i] = vtfft_im[i * num_rev]; } /* Done */ return; } /* ************************************************************** */ /* * vtcoil_check_vthar * This function checks the harmonics for large noise levels. * It compares the harmonic signals to signals between the harmonics. * * Input: * num_samp, number of integrated voltage samples * vtfft_re[0 to num_samp - 1], real part of FFT coeficients * vtfft_im[0 to num_samp - 1], imaginary part of FFT coeficients * num_rev, number of coil revolutions in the samples * num_har, number of harmonics * vthar_re[0 to num_har], real part of vt harmonics * vthar_im[0 to num_har], imaginary part of vt harmonics * * Output: * ok, 'y' if data looks ok, 'n' otherwise * * Zachary Wolf * 10/8/98 */ char vtcoil_check_vthar(int num_samp, double vtfft_re[], double vtfft_im[], int num_rev, int num_har) { /* Declare variables */ char ok; int num_noisy_har; int i; double har; double noise_above, noise_below, noise; FILE* file_ptr; /* Initialize */ ok = 'y'; num_noisy_har = 0; /* Check the input parameters */ if (num_rev < 4) return ok; /* Method fails */ if (num_har <= 10) return ok; /* Method fails */ if (num_har * num_rev > num_samp / 2) { vtcoil_error("vtcoil_check_vthar: improper num_har"); return ok; } /* Check the first 10 harmonics for noise */ /* The harmonics are at 1, 2, 3, ... cycles per revolution */ num_noisy_har = 0.; for (i = 1; i <= 10; i++) { har = sqrt(pow(vtfft_re[i * num_rev], 2) + pow(vtfft_im[i * num_rev], 2)); noise_above = sqrt(pow(vtfft_re[i * num_rev + 2], 2) + pow(vtfft_im[i * num_rev + 2], 2)); noise_below = sqrt(pow(vtfft_re[i * num_rev - 2], 2) + pow(vtfft_im[i * num_rev - 2], 2)); noise = (noise_above + noise_below) / 2.; if (noise > 0.2 * har) num_noisy_har++; } /* Set the ok flag if noisy */ if (num_noisy_har > 1) ok = 'n'; /* Write the samples to a file if ok = 'n' */ if (ok == 'n') { file_ptr = fopen("vt_check_har.dat", "w"); if (file_ptr == NULL) vtcoil_error("Problem opening vt_check_har.dat"); for (i = 0; i < num_samp; i++) fprintf(file_ptr, "%f %f\n", vtfft_re[i], vtfft_im[i]); fclose(file_ptr); } /* Done */ return ok; } /* ************************************************************** */ /* * vtcoil_log_vthar * This function logs the result of a vt harmonics measurement. * * Input: * rot_dir, encoder rotation direction, '+' = CCW or '-' = CW * num_rev, number of revolutions to measure over * num_har, number of harmonics to record * vthar_re[0 to num_har], vt harmonics, real part, [0] is unused * vthar_im[0 to num_har], vt harmonics, imaginary part, [0] is unused * * Zachary Wolf * 10/6/98 */ void vtcoil_log_vthar(char rot_dir, int num_rev, int num_har, double vthar_re[], double vthar_im[]) { /* Declare variables */ FILE* file_ptr; int i; int num_print = 12; double vthar_mag, vthar_ph; /* Open the log file */ file_ptr = fopen(log_file, "a"); if (file_ptr == NULL) { vtcoil_message("Unable to open log file"); return; } /* Determine how many harmonics to print */ if (num_har < num_print) num_print = num_har; /* Write the measured values to the log file */ fprintf(file_ptr, "%s VT Harmonics Measurement\n", TimeStr()); fprintf(file_ptr, " rot_dir = %c, num_rev = %i\n", rot_dir, num_rev); fprintf(file_ptr, " i vthar_re[i] vthar_im[i] vthar_mag[i] vthar_ph[i] \n"); fprintf(file_ptr, " (Vs) (Vs) (Vs) (deg) \n"); fprintf(file_ptr, " --- ------------ ------------ ------------ ------------\n"); if (num_har < num_print) num_print = num_har; for (i = 1; i <= num_print; i++) { vthar_mag = sqrt(pow(vthar_re[i], 2) + pow(vthar_im[i], 2)); vthar_ph = 180. / 3.1415927 * atan2(vthar_im[i], vthar_re[i]); fprintf(file_ptr, " %3i %12.7f %12.7f %12.7f %12.7f\n", i, vthar_re[i], vthar_im[i], vthar_mag, vthar_ph); } /* Close the log file */ fclose(file_ptr); /* Done */ return; } /* ************************************************************** */ /* * vtcoil_log_vthar_ave * This function logs the result of a VT harmonics measurement. * It writes the average and rms variation of VT harmonics measurements * to the log file. * * Input: * num_har, number of harmonics to record * vthar_re_ave[0 to num_har], average of VT harmonics measurements (Vs), real part, [0] is unused * vthar_re_rms[0 to num_har], rms variation of VT harmonics measurements (Vs), real part, [0] is unused * vthar_im_ave[0 to num_har], average of VT harmonics measurements (Vs), imaginary part, [0] is unused * vthar_im_rms[0 to num_har], rms variation of VT harmonics measurements (Vs), imaginary part, [0] is unused * * Zachary Wolf * 7/7/99 */ void vtcoil_log_vthar_ave(int num_har, double vthar_re_ave[], double vthar_re_rms[], double vthar_im_ave[], double vthar_im_rms[]) { /* Declare variables */ FILE* file_ptr; int i; int num_print = 12; /* Open the log file */ file_ptr = fopen(log_file, "a"); if (file_ptr == NULL) { vtcoil_message("Unable to open log file"); return; } /* Determine how many harmonics to print */ if (num_har < num_print) num_print = num_har; /* Write the measured values to the log file */ fprintf(file_ptr, "%s Integrated Voltage Harmonics, Average and RMS of Measurements\n", TimeStr()); fprintf(file_ptr, " n vthar_re[i] svthar_re[i] vthar_im[i] svthar_im[i]\n"); fprintf(file_ptr, " (Vs) (Vs) (Vs) (Vs) \n"); fprintf(file_ptr, " --- ------------ ------------ ------------ ------------\n"); for (i = 1; i <= num_print; i++) { fprintf(file_ptr, " %3i %12.7f %12.7f %12.7f %12.7f\n", i, vthar_re_ave[i], vthar_re_rms[i], vthar_im_ave[i], vthar_im_rms[i]); } /* Close the log file */ fclose(file_ptr); /* Done */ return; } /* ************************************************************** */ /* * vtcoil_log_message * This function logs messages for the module. * * Input: * message, string to display in standard I/O * * Zachary Wolf * 10/12/98 */ void vtcoil_log_message(char* message) { /* Declare variables */ FILE* file_ptr; /* Open the log file */ file_ptr = fopen(log_file, "a"); if (file_ptr == NULL) { vtcoil_message("Unable to open log file"); return; } /* Write the message to the log file */ fprintf(file_ptr, " %s\n", message); /* Close the log file */ fclose(file_ptr); /* Done */ return; }