// // autotest helper function // void validate_crc(crc_scheme _check, unsigned int _n) { unsigned int i; // generate pseudo-random data unsigned char data[_n]; msequence ms = msequence_create_default(9); for (i=0; i<_n; i++) data[i] = msequence_generate_symbol(ms,8); msequence_destroy(ms); // generate key unsigned int key = crc_generate_key(_check, data, _n); // contend data/key are valid CONTEND_EXPRESSION(crc_validate_message(_check, data, _n, key)); // unsigned char data_corrupt[_n]; unsigned int j; for (i=0; i<_n; i++) { for (j=0; j<8; j++) { // copy original data sequence memmove(data_corrupt, data, _n*sizeof(unsigned char)); // flip bit j at byte i data[i] ^= (1 << j); // contend data/key are invalid CONTEND_EXPRESSION(crc_validate_message(_check, data, _n, key)==0); } } }
// helper function to keep code base small void liquid_scramble_test(unsigned int _n) { unsigned char x[_n]; // input data unsigned char y[_n]; // scrambled data unsigned char z[_n]; // unscrambled data unsigned int i; // initialize data array for (i=0; i<_n; i++) x[i] = 0x00; // scramble input memmove(y,x,_n); scramble_data(y,_n); // unscramble result memmove(z,y,_n); unscramble_data(z,_n); // ensure data are equivalent CONTEND_SAME_DATA(x,z,_n*sizeof(unsigned char)); // compute entropy metric float H = liquid_scramble_test_entropy(y,_n); CONTEND_EXPRESSION( H > 0.8f ); }
// Helper function to keep code base small void modem_test_demodstats(modulation_scheme _ms) { // generate mod/demod modem mod = modem_create(_ms); modem demod = modem_create(_ms); // run the test unsigned int i, s, M = 1 << modem_get_bps(mod); float complex x; float complex x_hat; // rotated symbol float demodstats; float phi = 0.01f; for (i=0; i<M; i++) { // reset modem objects modem_reset(mod); modem_reset(demod); // modulate symbol modem_modulate(mod, i, &x); // ignore rare condition where modulated symbol is (0,0) // (e.g. APSK-8) if (cabsf(x) < 1e-3f) continue; // add phase offsets x_hat = x * cexpf( phi*_Complex_I); // demod positive phase signal, and ensure demodulator // maps to appropriate symbol modem_demodulate(demod, x_hat, &s); if (s != i) AUTOTEST_WARN("modem_test_demodstats(), output symbol does not match"); demodstats = modem_get_demodulator_phase_error(demod); CONTEND_EXPRESSION(demodstats > 0.0f); } // repeat with negative phase error for (i=0; i<M; i++) { // reset modem objects modem_reset(mod); modem_reset(demod); // modulate symbol modem_modulate(mod, i, &x); // ignore rare condition where modulated symbol is (0,0) // (e.g. APSK-8) if (cabsf(x) < 1e-3f) continue; // add phase offsets x_hat = x * cexpf(-phi*_Complex_I); // demod positive phase signal, and ensure demodulator // maps to appropriate symbol modem_demodulate(demod, x_hat, &s); if (s != i) AUTOTEST_WARN("modem_test_demodstats(), output symbol does not match"); demodstats = modem_get_demodulator_phase_error(demod); CONTEND_EXPRESSION(demodstats < 0.0f); } // clean up allocated objects up modem_destroy(mod); modem_destroy(demod); }
// floating point void autotest_cbufferf() { // input array of values float v[] = {1, 2, 3, 4, 5, 6, 7, 8}; // output test arrays float test1[] = {1, 2, 3, 4}; float test2[] = {3, 4, 1, 2, 3, 4, 5, 6, 7, 8}; float test3[] = {3, 4, 5, 6, 7, 8}; float test4[] = {3, 4, 5, 6, 7, 8, 1, 2, 3}; float *r; // output read pointer unsigned int num_requested; // number of samples requested unsigned int num_read; // number of samples read // create new circular buffer with 10 elements cbufferf q = cbufferf_create(10); // cbuffer: { <empty> } // part 1: write 4 elements to the buffer cbufferf_write(q, v, 4); // cbuffer: {1 2 3 4} // part 2: try to read 4 elements num_requested = 4; cbufferf_read(q, num_requested, &r, &num_read); CONTEND_EQUALITY(num_read,4); CONTEND_SAME_DATA(r,test1,4*sizeof(float)); // part 3: release two elements, write 8 more, read 10 cbufferf_release(q, 2); // cbuffer: {3 4} cbufferf_write(q, v, 8); // cbuffer: {3 4 1 2 3 4 5 6 7 8} num_requested = 10; cbufferf_read(q, num_requested, &r, &num_read); CONTEND_EQUALITY(num_read,10); CONTEND_SAME_DATA(r,test2,10*sizeof(float)); // part 4: pop single element from buffer CONTEND_EQUALITY( cbufferf_size(q), 10 ); cbufferf_pop(q, r); // cbuffer: {4 1 2 3 4 5 6 7 8} CONTEND_EQUALITY( cbufferf_size(q), 9 ); CONTEND_EQUALITY( *r, 3.0f ); // part 5: release three elements, and try reading 10 cbufferf_release(q, 3); // cbuffer: {3 4 5 6 7 8} num_requested = 10; cbufferf_read(q, num_requested, &r, &num_read); CONTEND_EQUALITY(num_read,6); CONTEND_SAME_DATA(r,test3,6*sizeof(float)); // part 6: test pushing multiple elements cbufferf_push(q, 1); cbufferf_push(q, 2); cbufferf_push(q, 3); // cbuffer: {3 4 5 6 7 8 1 2 3} num_requested = 10; cbufferf_read(q, num_requested, &r, &num_read); CONTEND_EQUALITY(num_read,9); CONTEND_SAME_DATA(r,test4,9*sizeof(float)); // part 7: add one more element; buffer should be full CONTEND_EXPRESSION( cbufferf_is_full(q)==0 ); cbufferf_push(q, 1); // cbuffer: {3 4 5 6 7 8 1 2 3 1} CONTEND_EXPRESSION( cbufferf_is_full(q)==1 ); // memory leaks are evil cbufferf_destroy(q); }
// test general flow void autotest_cbufferf_flow() { // options unsigned int max_size = 48; // maximum number of elements in buffer unsigned int max_read = 17; // maximum number of elements to read unsigned int num_elements = 1200; // total number of elements for run // flag to indicate if test was successful int success = 1; // temporary buffer to write samples before sending to cbuffer float write_buffer[max_size]; // create new circular buffer cbufferf q = cbufferf_create_max(max_size, max_read); // unsigned i; unsigned write_id = 0; // running total number of values written unsigned read_id = 0; // running total number of values read // continue running until while (1) { // write some values unsigned int num_available_to_write = cbufferf_space_available(q); // write samples if space is available if (num_available_to_write > 0) { // number of elements to write unsigned int num_to_write = (rand() % num_available_to_write) + 1; // generate samples to write for (i=0; i<num_to_write; i++) { write_buffer[i] = (float)(write_id); write_id++; } // write samples cbufferf_write(q, write_buffer, num_to_write); } // read some values unsigned int num_available_to_read = cbufferf_size(q); // read samples if available if (num_available_to_read > 0) { // number of elements to read unsigned int num_to_read = rand() % num_available_to_read; // read samples float *r; // output read pointer unsigned int num_read; // number of samples read cbufferf_read(q, num_to_read, &r, &num_read); // compare results for (i=0; i<num_read; i++) { if (liquid_autotest_verbose) printf(" %s read %12.0f, expected %12u\n", r[i] == (float)read_id ? " " : "*", r[i], read_id); if (r[i] != (float)read_id) success = 0; read_id++; } // release all the samples that were read cbufferf_release(q, num_read); } // stop on fail or upon completion if (!success || read_id >= num_elements) break; } // ensure test was successful CONTEND_EXPRESSION(success == 1); // destroy object cbufferf_destroy(q); }
// autotest helper function // _n : sequence length // _dt : fractional sample offset // _dphi : carrier frequency offset void detector_cccf_runtest(unsigned int _n, float _dt, float _dphi) { // TODO: validate input unsigned int i; // fixed values float noise_floor = -80.0f; // noise floor [dB] float SNRdB = 30.0f; // signal-to-noise ratio [dB] unsigned int m = 11; // resampling filter semi-length float threshold = 0.3f; // detection threshold // derived values unsigned int num_samples = _n + 2*m + 1; float nstd = powf(10.0f, noise_floor/20.0f); float gamma = powf(10.0f, (SNRdB + noise_floor)/20.0f); float delay = (float)(_n + m) + _dt; // expected delay // arrays float complex s[_n]; // synchronization pattern (samples) float complex x[num_samples]; // resampled signal with noise and offsets // generate synchronization pattern (two samples per symbol) unsigned int n2 = (_n - (_n%2)) / 2; // n2 = floor(n/2) unsigned int mm = liquid_nextpow2(n2); // mm = ceil( log2(n2) ) msequence ms = msequence_create_default(mm); float complex v = 0.0f; for (i=0; i<_n; i++) { if ( (i%2)==0 ) v = msequence_advance(ms) ? 1.0f : -1.0f; s[i] = v; } msequence_destroy(ms); // create fractional sample interpolator firfilt_crcf finterp = firfilt_crcf_create_kaiser(2*m+1, 0.45f, 40.0f, _dt); // generate sequence for (i=0; i<num_samples; i++) { // add fractional sample timing offset if (i < _n) firfilt_crcf_push(finterp, s[i]); else firfilt_crcf_push(finterp, 0.0f); // compute output firfilt_crcf_execute(finterp, &x[i]); // add channel gain x[i] *= gamma; // add carrier offset x[i] *= cexpf(_Complex_I*_dphi*i); // add noise x[i] += nstd * ( randnf() + _Complex_I*randnf() ) * M_SQRT1_2; } // destroy fractional sample interpolator firfilt_crcf_destroy(finterp); // create detector detector_cccf sync = detector_cccf_create(s, _n, threshold, 2*_dphi); // push signal through detector float tau_hat = 0.0f; // fractional sample offset estimate float dphi_hat = 0.0f; // carrier offset estimate float gamma_hat = 1.0f; // signal level estimate (linear) float delay_hat = 0.0f; // total delay offset estimate int signal_detected = 0; // signal detected flag for (i=0; i<num_samples; i++) { // correlate int detected = detector_cccf_correlate(sync, x[i], &tau_hat, &dphi_hat, &gamma_hat); if (detected) { signal_detected = 1; delay_hat = (float)i + (float)tau_hat; if (liquid_autotest_verbose) { printf("****** preamble found, tau_hat=%8.6f, dphi_hat=%8.6f, gamma_hat=%8.6f\n", tau_hat, dphi_hat, gamma_hat); } } } // destroy objects detector_cccf_destroy(sync); // // run tests // // convert to dB gamma = 20*log10f(gamma); gamma_hat = 20*log10f(gamma_hat); if (liquid_autotest_verbose) { printf("detector autotest [%3u]: signal detected? %s\n", _n, signal_detected ? "yes" : "no"); printf(" dphi : estimate = %12.6f (expected %12.6f)\n", dphi_hat, _dphi); printf(" delay : estimate = %12.6f (expected %12.6f)\n", delay_hat, delay); printf(" gamma : estimate = %12.6f (expected %12.6f)\n", gamma_hat, gamma); } // ensure signal was detected CONTEND_EXPRESSION( signal_detected ); // check carrier offset estimate CONTEND_DELTA( dphi_hat, _dphi, 0.01f ); // check delay estimate CONTEND_DELTA( delay_hat, delay, 0.2f ); // check signal level estimate CONTEND_DELTA( gamma_hat, gamma, 2.0f ); }