int generate_init(rp_calib_params_t *calib_params) { gen_calib_params = calib_params; if(fpga_awg_init() < 0) { return -1; } ch1_max_dac_v = fpga_awg_calc_dac_max_v(gen_calib_params->be_ch1_fs); ch2_max_dac_v = fpga_awg_calc_dac_max_v(gen_calib_params->be_ch2_fs); return 0; }
/** * Synthesize a desired signal. * * Generates/synthesizes a signal, based on three predefined signal * types/shapes, signal amplitude & frequency. The data[] vector of * samples at 125 MHz is generated to be re-played by the FPGA AWG module. * * @param[in] ampl Signal amplitude [Vpp]. * @param[in] freq Signal frequency [Hz]. * @param[in] calib_dc_offs Calibrated Instrument DC offset * @param[in] max_dac_v Maximum DAC voltage in [V] * @param[in] user_dc_offs User defined DC offset * @param[in] type Signal type/shape [Sine, Square, Triangle]. * @param[in] data Returned synthesized AWG data vector. * @param[out] awg Returned AWG parameters. */ void synthesize_signal(float ampl, float freq, int calib_dc_offs, int calib_fs, float max_dac_v, float user_dc_offs, awg_signal_t type, int32_t *data, awg_param_t *awg) { uint32_t i; /* Various locally used constants - HW specific parameters */ const int trans0 = 30; const int trans1 = 300; const float tt2 = 0.249; const int c_dac_max = (1 << (c_awg_fpga_dac_bits - 1)) - 1; const int c_dac_min = -(1 << (c_awg_fpga_dac_bits - 1)); int trans = round(freq / 1e6 * ((float) trans1)); /* 300 samples at 1 MHz */ int user_dc_off_cnt = round((1<<(c_awg_fpga_dac_bits-1)) * user_dc_offs / max_dac_v); uint32_t amp; /* Saturate offset - depending on calibration offset, it could overflow */ int offsgain = calib_dc_offs + user_dc_off_cnt; offsgain = (offsgain > c_dac_max) ? c_dac_max : offsgain; offsgain = (offsgain < c_dac_min) ? c_dac_min : offsgain; awg->offsgain = (offsgain << 16) | 0x2000; awg->step = round(65536.0 * freq/c_awg_smpl_freq * ((float) AWG_SIG_LEN)); awg->wrap = round(65536 * (AWG_SIG_LEN - 1)); //= (ampl) * (1<<(c_awg_fpga_dac_bits-2)); //fpga_awg_calc_dac_max_v(calib_fs) amp= round(ampl/2/fpga_awg_calc_dac_max_v(calib_fs)* c_dac_max ); /* Truncate to max value */ amp = (amp > c_dac_max) ? c_dac_max : amp; if (trans <= 10) { trans = trans0; } /* Fill data[] with appropriate buffer samples */ for(i = 0; i < AWG_SIG_LEN; i++) { /* Sine */ if (type == eSignalSine) { data[i] = round(amp * cos(2*M_PI*(float)i/(float)AWG_SIG_LEN)); } /* Square */ if (type == eSignalSquare) { data[i] = round(amp * cos(2*M_PI*(float)i/(float)AWG_SIG_LEN)); data[i] = (data[i] > 0) ? amp : -amp; /* Soft linear transitions */ float mm, qq, xx, xm; float x1, x2, y1, y2; xx = i; xm = AWG_SIG_LEN; mm = -2.0*(float)amp/(float)trans; qq = (float)amp * (2 + xm/(2.0*(float)trans)); x1 = xm * tt2; x2 = xm * tt2 + (float)trans; if ( (xx > x1) && (xx <= x2) ) { y1 = (float)amp; y2 = -(float)amp; mm = (y2 - y1) / (x2 - x1); qq = y1 - mm * x1; data[i] = round(mm * xx + qq); } x1 = xm * 0.75; x2 = xm * 0.75 + trans; if ( (xx > x1) && (xx <= x2)) { y1 = -(float)amp; y2 = (float)amp; mm = (y2 - y1) / (x2 - x1); qq = y1 - mm * x1; data[i] = round(mm * xx + qq); } } /* Triangle */ if (type == eSignalTriangle) { data[i] = round(-1.0 * (float)amp * (acos(cos(2*M_PI*(float)i/(float)AWG_SIG_LEN))/M_PI*2-1)); } } }
/** Signal generator main */ int main(int argc, char *argv[]) { g_argv0 = argv[0]; if ( argc < 4 ) { usage(); return -1; } /* Channel argument parsing */ uint32_t ch = atoi(argv[1]) - 1; /* Zero based internally */ if (ch > 1) { fprintf(stderr, "Invalid channel: %s\n", argv[1]); usage(); return -1; } /* Signal amplitude argument parsing */ float ampl = strtod(argv[2], NULL); if ( (ampl < 0.0) || (ampl > c_max_amplitude) ) { fprintf(stderr, "Invalid amplitude: %s\n", argv[2]); usage(); return -1; } /* Signal frequency argument parsing */ float freq = strtod(argv[3], NULL); if ( (freq < 0.0) || (freq > c_max_frequency ) ) { fprintf(stderr, "Invalid frequency: %s\n", argv[3]); usage(); return -1; } /* Signal type argument parsing */ awg_signal_t type = eSignalSine; if (argc > 4) { if ( strcmp(argv[4], "sine") == 0) { type = eSignalSine; } else if ( strcmp(argv[4], "sqr") == 0) { type = eSignalSquare; } else if ( strcmp(argv[4], "tri") == 0) { type = eSignalTriangle; } else { fprintf(stderr, "Invalid signal type: %s\n", argv[4]); usage(); return -1; } } //Added by Marko float offsetVolts = 0.0; if(argc > 5){ offsetVolts = strtod(argv[5], NULL); //fprintf(stderr, "Using offset: %f\n", offsetVolts); } rp_default_calib_params(&rp_calib_params); gen_calib_params = &rp_calib_params; if(rp_read_calib_params(gen_calib_params) < 0) { fprintf(stderr, "rp_read_calib_params() failed, using default" " parameters\n"); } ch1_max_dac_v = fpga_awg_calc_dac_max_v(gen_calib_params->be_ch1_fs); ch2_max_dac_v = fpga_awg_calc_dac_max_v(gen_calib_params->be_ch2_fs); awg_param_t params; /* Prepare data buffer (calculate from input arguments) */ synthesize_signal(ampl,//Amplitude freq,//Frequency (ch == 0) ? gen_calib_params->be_ch1_dc_offs : gen_calib_params->be_ch2_dc_offs,//Calibrated Instrument DC offset (ch == 0) ? gen_calib_params->be_ch1_fs : gen_calib_params->be_ch2_fs,//Calibrated Back-End full scale coefficient (ch == 0) ? ch1_max_dac_v : ch2_max_dac_v,//Maximum DAC voltage offsetVolts,//DC offset voltage type,//Signal type/shape data,//Returned synthesized AWG data vector ¶ms);//Returned AWG parameters /* Write the data to the FPGA and set FPGA AWG state machine */ write_data_fpga(ch, data, ¶ms); }
/** * @brief Calculate Signal Shape based on Time Based Signal definition * Function is intended to calculate the shape of utput Signal for the individual channel, * i.e. a function must be called separatelly for each signal. * Time Based Signal definition is taken from input parameters in_data. time_vect and * in_data_len arguments. The output signal shape is additionally parameterized with * amp, calib_dc_offs, max_dac_v and user_dc_offs arguments, which depict properties of * referenced channel. Calculated Signal Shape is returned in the specified out_data buffer. * Beside of shape calculation the apparent AWG settings for referenced channel are calculated. * * @param[in] in_data Array of signal values, applied at apparent time from Time Based vector * @param[in] time_vect Array, specifying Time Based values * @param[in] in_data_len Number of valid entries in the specified buffers * @param[in] amp Maximal amplitude of calculated Signal * @param[in] calib_dc_offs Calibrated DC offset, specified in ADC counts * @param[in] max_dac_v Maximal Voltage on DAC outputs, expressed in [V] * @param[in] user_dc_offs Configurable DC offset, expressed in [V] * @param[out] out_data Output Signal Shape data buffer * @param[out] awg Modified AWG settings with updated offsgain, step and wrap parameters * @retval -1 Failure, error message is output on standard error * @retval 0 Success */ int calculate_data(float *in_data, int in_data_len, float amp, float freq, int calib_dc_offs, uint32_t calib_fs, float max_dac_v, float user_dc_offs, int32_t *out_data, awg_param_t *awg) { const int c_dac_max = (1 << (c_awg_fpga_dac_bits - 1)) - 1; const int c_dac_min = -(1 << (c_awg_fpga_dac_bits - 1)); float max_amp, min_amp; float k_norm; int i, j; /* calculate configurable DC offset, expressed in ADC counts */ int user_dc_off_cnt = round((1<<(c_awg_fpga_dac_bits-1)) * user_dc_offs / max_dac_v); /* partial check for validity of input parameters */ if((in_data == NULL) || (out_data == NULL) || (awg == NULL) || (in_data_len >= AWG_SIG_LEN)) { fprintf(stderr, "Internal error, the Time Based signal definition is not correctly specified.\n"); return -1; } /* Saturate offset - depending on calibration offset, it could overflow */ int offsgain = calib_dc_offs + user_dc_off_cnt; offsgain = (offsgain > c_dac_max) ? c_dac_max : offsgain; offsgain = (offsgain < c_dac_min) ? c_dac_min : offsgain; /* modify AWG settings */ awg->offsgain = (offsgain << 16) | 0x2000; awg->step = round(65536 * freq/c_awg_smpl_freq * in_data_len); awg->wrap = round(65536 * (in_data_len - 1)); /* Retrieve max amplitude of the specified Signal Definition, it is used for the normalization */ max_amp = -1e30; min_amp = +1e30; for(i = 0; i < in_data_len; i++) { max_amp = (in_data[i] > max_amp) ? in_data[i] : max_amp; min_amp = (in_data[i] < min_amp) ? in_data[i] : min_amp; } /* Calculate normalization factor */ if ((max_amp - min_amp) == 0) { k_norm = (max_amp == 0) ? 0 : (float)(c_dac_max) * amp /(max_amp*2) / fpga_awg_calc_dac_max_v(calib_fs); } else { k_norm = (float)(c_dac_max) * amp /(max_amp - min_amp) / fpga_awg_calc_dac_max_v(calib_fs); } /* Normalize Signal values */ for(i = 0; i < in_data_len; i++) { out_data[i] = round(k_norm * (in_data[i] - (max_amp + min_amp)/2)); /* Clipping */ if (out_data[i] > c_dac_max) out_data[i] = c_dac_max; if (out_data[i] < c_dac_min) out_data[i] = c_dac_min; } /* ...and pad it with zeros */ // TODO: Really from j = i+1 ??? Should be from j = i IMHO. for(j = i+1; j < AWG_SIG_LEN; j++) { out_data[j] = 0; } return 0; }
/** * @brief Calculate Signal Shape based on Time Based Signal definition * Function is intended to calculate the shape of utput Signal for the individual channel, * i.e. a function must be called separatelly for each signal. * Time Based Signal definition is taken from input parameters in_data. time_vect and * in_data_len arguments. The output signal shape is additionally parameterized with * amp, calib_dc_offs, max_dac_v and user_dc_offs arguments, which depict properties of * referenced channel. Calculated Signal Shape is returned in the specified out_data buffer. * Beside of shape calculation the apparent AWG settings for referenced channel are calculated. * * @param[in] in_data Array of signal values, applied at apparent time from Time Based vector * @param[in] time_vect Array, specifying Time Based values * @param[in] in_data_len Number of valid entries in the specified buffers * @param[in] amp Maximal amplitude of calculated Signal * @param[in] calib_dc_offs Calibrated DC offset, specified in ADC counts * @param[in] max_dac_v Maximal Voltage on DAC outputs, expressed in [V] * @param[in] user_dc_offs Configurable DC offset, expressed in [V] * @param[out] out_data Output Signal Shape data buffer * @param[out] awg Modified AWG settings with updated offsgain, step and wrap parameters * @retval -1 Failure, error message is output on standard error * @retval 0 Success */ int calculate_data(float *in_data, int in_data_len, float amp, float freq, int calib_dc_offs, uint32_t calib_fs, float max_dac_v, float user_dc_offs, int32_t *out_data, awg_param_t *awg) { float max_amp,min_amp; float k_norm; int i; /* calculate configurable DC offset, expressed in ADC counts */ int user_dc_off_cnt = round((1<<(c_awg_fpga_dac_bits-1)) * user_dc_offs / max_dac_v); /* partial check for validity of input parameters */ if((in_data == NULL) || (out_data == NULL) || (awg == NULL) || (in_data_len >= AWG_SIG_LEN)) { fprintf(stderr, "Internal error, the Time Based signal definition is not correctly specified.\n"); return -1; } /* modify AWG settings */ awg->offsgain = ((calib_dc_offs+user_dc_off_cnt) << 16) | 0x2000; awg->step = round(65536 * freq/c_awg_smpl_freq * in_data_len); awg->wrap = round(65536 * (in_data_len-1)); /* retrieve max amplitude of the specified Signal Definition, it is used for the normalization */ max_amp = -1e30; min_amp = +1e30; for(i = 0; i < in_data_len; i++) { if((in_data[i]) > max_amp) max_amp = in_data[i]; if((in_data[i]) < min_amp) min_amp = in_data[i]; } /* calculate normalization factor */ if (max_amp-min_amp==0) { if (max_amp==0) k_norm=0; else k_norm= (float)((1<<(c_awg_fpga_dac_bits-1))-1) * amp /(max_amp*2) /fpga_awg_calc_dac_max_v(calib_fs); } else k_norm = (float)((1<<(c_awg_fpga_dac_bits-1))-1) * amp /(max_amp-min_amp) /fpga_awg_calc_dac_max_v(calib_fs); /* normalize Signal values */ for(i = 0; i < in_data_len; i++) { out_data[i] = round(k_norm * (in_data[i]-(max_amp+min_amp)/2)); // clipping if(out_data[i]>(1<<(c_awg_fpga_dac_bits-1))-1) out_data[i]=(1<<(c_awg_fpga_dac_bits-1))-1; if(out_data[i]<(-(1<<(c_awg_fpga_dac_bits-1)))) out_data[i]=(-(1<<(c_awg_fpga_dac_bits-1))); if(out_data[i] < 0) out_data[i] += (1 << 14); } /* ...and pad it with zero */ for(i = i+1; i < AWG_SIG_LEN; i++) { out_data[i] = 0; } return 0; }