00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include <stdlib.h>
00024 #include <stdio.h>
00025 #include <pthread.h>
00026
00027 #include <gsl/gsl_errno.h>
00028 #include <gsl/gsl_fft_real.h>
00029 #include <gsl/gsl_fft_halfcomplex.h>
00030
00031 #include "agc.h"
00032 #include "dt_window.h"
00033 #include "dglitch.h"
00034
00035 #include "morse.h"
00036 #include "utility.h"
00037 #include "fir_win.h"
00038
00040 #define REAL(z,i) ((z)[i])
00041 #define IMAG(z,i,length) ((i) == 0 ? 0 : (z)[length-i])
00042
00043 #define THIRTY_SIX_WPM (double)36.000
00044
00047 #define DIT_DUR_AT_36_WPM (double)00.0325
00048
00049 #define WPM_SECONDS_PER_DIT (double)(DIT_DUR_AT_36_WPM * THIRTY_SIX_WPM)
00050
00054 #define MIN_NUM_PACKS_PER_DIT 6
00055
00057 #define FFT_GRAPH_TOP (double)100000.0
00058
00059 #define CW_ON_AMPLITUDE (FFT_GRAPH_TOP * 0.51)
00060
00061 #define CW_OFF_AMPLITUDE (FFT_GRAPH_TOP * 0.5)
00062 #define MORSE_AGC_TYPE AGC_LINEAR_PEAK_W_COMPUTED_GAIN_ON_CLIP
00063 #define FFT_DATA_MAX_GAIN (double)10.0
00064 #define FFT_DATA_MIN_GAIN (double)0.1
00065 #define AGC_FULL_SCALE_DELAY_SECS (double)3.0
00066 #define FFT_NOISE_FLOOR (double)50000.0
00067 #define FFT_SCOPE_GAIN_UP_CRR_FACTOR (double)0.025
00068 #define FFT_SCOPE_GAIN_DOWN_CRR_FACTOR (double)0.05
00069
00070 #define AUDIO_SEGMENTS_PER_STATUS_UPDATE 5
00071
00072
00073 #if !(defined(FALSE) || defined(TRUE))
00074 #define FALSE 0
00075 #define TRUE 1
00076 #endif
00077
00078
00079 #define CLEAR_CHARACTER_STATUS 0x00u
00080 #define END_OF_CHARACTER 0x01u
00081 #define VALID_CW_ON_TONE_DETECTED 0x02u
00082 #define VALID_CW_OFF_TONE_DETECTED 0x04u
00083
00084
00085 #define CLEAR_WORD_STATUS 0x00u
00086 #define END_OF_WORD 0x01u
00087
00088 #define SHORT_DAH_VS_DITS (double)2.4000
00089 #define LONG_DAH_VS_DITS (double)3.6000
00090 #define SHORT_DIT_VS_DAHS (double)0.2667
00091 #define LONG_DIT_VS_DAHS (double)0.4000
00092
00093 #define SHORT_ELEMENT (double)0.7500
00094 #define LONG_ELEMENT (double)1.2500
00095
00096 #define CHARACTER_SPACE_DITS (double)2.5000
00097 #define WORD_SPACE_DITS (double)6.0000
00098
00099 #define MORSE_ELEMENT_HISTORY 5
00100
00101
00102
00103
00104 #define DEBUG_DIT_DAH_DETECTION(a)
00105 #define DEBUG_MORSE_STATISTICS(a)
00106 #define DEBUG_MORSE_TIMING(a)
00107 #define DEBUG_TONE_DETECTION(a)
00108 #define DEBUG_RAW_FFT_ARRAY(a)
00109 #define DEBUG_DGLITCHED_FFT_ARRAY(a)
00110 #define DEBUG_DATA_COLLECTION_THREAD(a)
00111 #define DEBUG_AUDIO_PP_THREAD(a)
00112 #define DEBUG_MORSE_THREAD(a)
00113
00114
00115
00116
00120 typedef struct IMorse {
00122 UI_update_status cb_morse_status;
00124 UI_scope_plot cb_scope_plot;
00126 UI_display_character cb_display_character;
00128 UI_debug_plot debug_plot;
00130 UI_Morse_statistics cb_Morse_statistics;
00131 } IMorse;
00132
00133 static IMorse user_interface;
00134
00135 static te_morse_plot_type scope_or_fft = PLOT_FFT;
00136
00140 typedef struct ts_morse_2_char
00141 {
00150 uint32_t morse;
00152 char *character;
00153 } ts_morse_2_char;
00154
00159 const ts_morse_2_char MORSE_CODE_DECODE_TABLE[] = {
00160 {0x0001, "e"},
00161 {0x8001, "t"},
00162 {0x0002, "i"},
00163 {0xC002, "m"},
00164 {0x8002, "n"},
00165 {0x4002, "a"},
00166 {0x0003, "s"},
00167 {0x8003, "d"},
00168 {0xC003, "g"},
00169 {0x2003, "u"},
00170 {0x4003, "r"},
00171 {0xA003, "k"},
00172 {0x6003, "w"},
00173 {0xE003, "o"},
00174 {0x8004, "b"},
00175 {0xA004, "c"},
00176 {0x2004, "f"},
00177 {0x0004, "h"},
00178 {0x7004, "j"},
00179 {0x4004, "l"},
00180 {0xB004, "y"},
00181 {0x6004, "p"},
00182 {0xD004, "q"},
00183 {0x1004, "v"},
00184 {0x9004, "x"},
00185 {0xC004, "z"},
00186 {0xF805, "0"},
00187 {0x7805, "1"},
00188 {0x3805, "2"},
00189 {0x1805, "3"},
00190 {0x0805, "4"},
00191 {0x0005, "5"},
00192 {0x8005, "6"},
00193 {0xC005, "7"},
00194 {0xE005, "8"},
00195 {0xF005, "9"},
00196 {0x8406, "-"},
00197 {0x9005, "/"},
00198 {0x5406, "."},
00199 {0xCC06, ","},
00200 {0x3006, "?"},
00201 {0x8805, "="},
00202 {0x6806, "@"},
00203 {0xB005, "("},
00204 {0xB406, ")"},
00205 {0x3406, "_"},
00206 {0x4806,"\""},
00207 {0x7806, "'"},
00208 {0xE006, ":"},
00209 {0xA806, ";"},
00210 {0x1207, "$"},
00211 {0x5005, "+"},
00212 {0xE004, "!"},
00213 {0x4005, "&"}
00214 };
00215 #define MORSE_CODE_DECODE_TABLE_SIZE (sizeof(MORSE_CODE_DECODE_TABLE) / sizeof(struct ts_morse_2_char))
00216
00221 typedef enum {CW_ON, CW_OFF} te_cw;
00222
00224 typedef enum {DAH, DIT} te_morse_element_type;
00225
00227 typedef struct ts_morse_settings {
00232 uint32_t tone_packet_size;
00233
00235 uint32_t fft_data_size;
00238 double minimum_tone_duration;
00240 double tone_packet_duration;
00242 double fft_freq_step;
00244 uint32_t fft_amp_pa_size;
00246 int32_t audio_type_id;
00248 p_audio_device audio_rec_dev;
00250 p_audio_device audio_play_dev;
00252 int32_t sample_frequency;
00253 } ts_morse_settings;
00254
00256 typedef struct ts_morse_statistics
00257 {
00259 double duration_history[MORSE_ELEMENT_HISTORY];
00261 double avg_duration;
00263 int32_t samples;
00264 } ts_morse_statistics;
00265
00267 typedef struct ts_morse_element
00268 {
00270 te_morse_element_type dah_or_dit;
00272 double duration;
00274 double previous_duration;
00275 } ts_morse_element;
00276
00278 typedef struct ts_morse_char
00279 {
00281 uint8_t dit_dahs;
00283 uint8_t element_position_mask;
00285 uint8_t elements;
00287 uint8_t char_status;
00289 uint8_t word_status;
00290 } ts_morse_char, *p_morse_char;
00291
00293 typedef struct ts_morse_data {
00295 dt_window dt_w;
00297 double agc_dg;
00299 te_morse_agc_enable apply_agc;
00301 agc scope_gain;
00303 double fft_data_gain;
00305 ts_morse_statistics dit_statistics;
00307 ts_morse_statistics dah_statistics;
00309 int32_t cw_frequency;
00311 double cw_on_duration, cw_off_duration;
00313 te_cw state;
00315 ts_morse_element morse_element;
00317 ts_morse_char morse_char;
00319 double words_per_minute;
00321 uint32_t filter_taps;
00323 double filter_bandwidth;
00325 p_fwin fir_filter;
00327 te_morse_filter_enable apply_fir;
00329 p_dglitch packet_amp_array_dg;
00331 double *fft_amp_pa;
00332 } ts_morse_data, *p_morse_data;
00333
00334 static ts_morse_settings morse_settings;
00335
00336
00337 static ts_morse_data morse_thread;
00338
00339
00340 static double *fft_data;
00341 static double *audio_data;
00342
00343 static volatile te_morse_thread_state run = THREADS_NOT_RUNNING;
00344
00346 typedef enum {
00348 DATA_COLLECTION_THREAD = 0,
00350 AUDIO_POST_PROCESSING_THREAD,
00352 MORSE_CODE_DECODING_THREAD,
00353 NUMBER_OF_THREADS
00354 } te_morse_threads;
00356 static pthread_t tid[NUMBER_OF_THREADS];
00357
00358 static pthread_mutex_t m_audio_post_processing = PTHREAD_MUTEX_INITIALIZER;
00359 static pthread_cond_t audio_post_processing_complete = PTHREAD_COND_INITIALIZER;
00360
00362 static int32_t fft_performed = 0;
00363 static int32_t samples_processed = 0;
00364
00365
00366
00367
00368
00370 static void data_collection(void *param);
00372 static void audio_post_process(void *param);
00374 static void morse_code(void * param);
00375
00377 static void free_all(void);
00378
00379
00380 static void morse_create_fft_amp_array(p_morse_data morse_thread_data);
00381 static void duration_2_morse_element(p_morse_data morse_thread_data);
00382 static void check_for_eoc_eow(p_morse_data morse_thread_data);
00383 static char *morse2txt(p_morse_char morse_char);
00384 static void reset_morse_char(p_morse_char morse_char);
00385 static void compute_morse_statistics(ts_morse_statistics *dit_dah, double duration);
00386 static void add_element_2_morse_character(p_morse_data morse_thread_data);
00387 static void morse_measure_fft_packet_timing(p_morse_data morse_thread_data);
00388 static void morse_calculate_current_wpm(p_morse_data morse_thread_data);
00389 static void morse_recompute_filter(void);
00390
00392 static void update_status(int32_t ffts, uint32_t samples, double wpm);
00394 static void scope_plot(double *vector, uint32_t num_points);
00396 static void display_character(char *character);
00398 static void update_morse_statistics(p_morse_data morse_data_p);
00399
00400
00401 #if DEBUG_GRAPH_INTERFACE == 1
00402 static void morse_debug_plot(const int8_t *plot, int32_t y);
00403 static void morse_debug_plot(const int8_t *plot, int32_t y)
00404 {
00405 if(user_interface.debug_plot != NULL) {user_interface.debug_plot(plot, y);}
00406 }
00407
00408 void register_debug_plot(UI_debug_plot hndlr) {user_interface.debug_plot = hndlr;}
00409 #else
00410 #define morse_debug_plot(a, b)
00411 #endif
00412
00413 int32_t morse_start_threads(void)
00414 {
00415 int32_t error = 0;
00416
00417 if(run != THREADS_NOT_RUNNING) {
00418 goto morse_running;
00419 }
00420 run = THREADS_STARTING;
00421
00422 morse_thread.agc_dg = (FFT_DATA_MAX_GAIN - FFT_DATA_MIN_GAIN) * (double)morse_settings.tone_packet_size \
00423 / AGC_FULL_SCALE_DELAY_SECS / (double)morse_settings.sample_frequency;
00424
00425 fft_performed = samples_processed = 0;
00426
00427
00428 if(audio_open(morse_settings.audio_rec_dev, morse_settings.sample_frequency) != 0) {
00429 goto err_hndlr;
00430 }
00431
00432
00433 morse_settings.minimum_tone_duration = \
00434 (double)morse_settings.tone_packet_size / (double)audio_sample_rate_get(morse_settings.audio_rec_dev) * (double)MIN_NUM_PACKS_PER_DIT;
00435
00436
00437 morse_settings.fft_amp_pa_size = (int32_t)audio_device_samples_get(morse_settings.audio_rec_dev) / (int32_t)morse_settings.tone_packet_size;
00438 morse_thread.dit_statistics.samples = 0;
00439 morse_thread.dah_statistics.samples = 0;
00440
00441
00442 if(NULL == (fft_data = malloc(morse_settings.tone_packet_size * sizeof(double)))) {
00443 goto err_hndlr;
00444 }
00445
00446
00447 if(NULL == (morse_thread.dt_w = dt_window_create(DT_HAMMING_WINDOW, morse_settings.tone_packet_size))) {
00448 goto err_hndlr;
00449 }
00450
00451
00452 if(NULL == (morse_thread.scope_gain = agc_create(FFT_DATA_MAX_GAIN,
00453 FFT_DATA_MIN_GAIN,
00454 FFT_NOISE_FLOOR,
00455 FFT_GRAPH_TOP,
00456 MORSE_AGC_TYPE,
00457 morse_thread.agc_dg,
00458 FFT_SCOPE_GAIN_DOWN_CRR_FACTOR))) {
00459 goto err_hndlr;
00460 }
00461
00462
00463 if(NULL == (morse_thread.packet_amp_array_dg = dglitch_create(DG_MEDIAN, MIN_NUM_PACKS_PER_DIT))) {
00464 goto err_hndlr;
00465 }
00466
00467
00468 morse_settings.tone_packet_duration = 1.0 / (double)morse_settings.sample_frequency * (double)morse_settings.tone_packet_size;
00469
00470 morse_settings.fft_freq_step = (double)morse_settings.sample_frequency / (double)morse_settings.tone_packet_size;
00471
00472
00473 if(NULL == (morse_thread.fir_filter = fwin_create((double)morse_settings.sample_frequency,
00474 500.0,
00475 1500.0,
00476 FW_BANDPASS,
00477 FW_BLACKMAN,
00478 morse_thread.filter_taps))) {
00479 goto err_hndlr;
00480 }
00481
00482 morse_recompute_filter();
00483
00484
00485 if(NULL == (morse_thread.fft_amp_pa = malloc(morse_settings.fft_amp_pa_size * sizeof(double)))) {
00486 goto err_hndlr;
00487 }
00488
00489
00490 if(NULL == (audio_data = malloc(audio_device_samples_get(morse_settings.audio_rec_dev) * sizeof(double)))) {
00491 goto err_hndlr;
00492 }
00493
00494 run = THREADS_RUNNING;
00495
00496
00497
00498
00499
00500 (void)pthread_create(&tid[MORSE_CODE_DECODING_THREAD], NULL, (void*)&morse_code, (void*)&morse_thread);
00501 (void)pthread_create(&tid[DATA_COLLECTION_THREAD], NULL, (void*)&data_collection, NULL);
00502 (void)pthread_create(&tid[AUDIO_POST_PROCESSING_THREAD], NULL, (void*)&audio_post_process, NULL);
00503
00510
00511 goto morse_running;
00512
00513 err_hndlr:
00514 error = -1;
00515 free_all();
00516 run = THREADS_NOT_RUNNING;
00517
00518 morse_running:
00519 return error;
00520 }
00521
00522 te_morse_thread_state morse_stop_threads(void)
00523 {
00524 if(run == THREADS_RUNNING) {
00525 run = THREADS_CANCELING;
00526 pthread_join(tid[DATA_COLLECTION_THREAD], NULL);
00527 pthread_join(tid[AUDIO_POST_PROCESSING_THREAD], NULL);
00528 pthread_join(tid[MORSE_CODE_DECODING_THREAD], NULL);
00529 free_all();
00530 run = THREADS_NOT_RUNNING;
00531 }
00532 return run;
00533 }
00534
00535 static void free_all(void)
00536 {
00537 dt_window_destroy(&morse_thread.dt_w);
00538 agc_destroy(&morse_thread.scope_gain);
00539 dglitch_destroy(&morse_thread.packet_amp_array_dg);
00540 fwin_destroy(&morse_thread.fir_filter);
00541 audio_close(morse_settings.audio_rec_dev);
00542 free(fft_data);
00543 free(audio_data);
00544 free(morse_thread.fft_amp_pa);
00545 fft_data = NULL;
00546 morse_thread.fft_amp_pa = NULL;
00547 }
00548
00549 static void data_collection(void *param)
00550 {
00551 static uint32_t buffer_segment;
00552
00553 param = param;
00554
00555 buffer_segment = BUFFER_SEGMENT_1;
00556 while(run == THREADS_RUNNING) {
00557 audio_capture(morse_settings.audio_rec_dev, buffer_segment);
00558 DEBUG_DATA_COLLECTION_THREAD( ("dc acq seg %d.\n", buffer_segment) );
00559 buffer_segment++;
00560 if(buffer_segment >= AUDIO_BUFFER_SEGMENTS) {
00561 buffer_segment = BUFFER_SEGMENT_1;
00562 }
00563 }
00564 pthread_exit(NULL);
00565 }
00566
00567 static void audio_post_process(void *param)
00568 {
00569 static te_audio_segments buffer_segment;
00570
00571 param = param;
00572
00573 buffer_segment = BUFFER_SEGMENT_1;
00574 while(run == THREADS_RUNNING) {
00575 DEBUG_AUDIO_PP_THREAD( ("audio pp wait seg %d.\n", buffer_segment) );
00576 audio_capture_wait(morse_settings.audio_rec_dev, buffer_segment);
00577 audio_raw2double(morse_settings.audio_rec_dev, buffer_segment, audio_data);
00578 DEBUG_AUDIO_PP_THREAD( ("audio pp done seg %d.\n", buffer_segment) );
00579 pthread_cond_broadcast(&audio_post_processing_complete);
00580 buffer_segment++;
00581 samples_processed += audio_device_samples_get(morse_settings.audio_rec_dev);
00582 if(buffer_segment >= AUDIO_BUFFER_SEGMENTS) {
00583 buffer_segment = BUFFER_SEGMENT_1;
00584 }
00585
00586 if(scope_or_fft == PLOT_SCOPE) {
00587 scope_plot(audio_data, morse_settings.tone_packet_size);
00588 }
00589 }
00590 pthread_cond_signal(&audio_post_processing_complete);
00591 pthread_exit(NULL);
00592 }
00593
00594 static void morse_code(void *param)
00595 {
00596 p_morse_data tmp = ((p_morse_data)(param));
00597
00598 while(run == THREADS_RUNNING) {
00599 DEBUG_MORSE_THREAD( ("wait for audio post process\n") );
00600 pthread_cond_wait(&audio_post_processing_complete, &m_audio_post_processing);
00601
00602 if(tmp->apply_fir == MORSE_ENABLE_FILTER) {
00603 fwin_apply(tmp->fir_filter, audio_data, audio_device_samples_get(morse_settings.audio_rec_dev));
00604 }
00605 DEBUG_MORSE_THREAD( ("FIR done\n") );
00606
00607
00608
00609
00610 morse_create_fft_amp_array(tmp);
00611 pthread_mutex_unlock(&m_audio_post_processing);
00612
00613
00614 dglitch_apply(tmp->packet_amp_array_dg, tmp->fft_amp_pa, morse_settings.fft_amp_pa_size);
00615 DEBUG_MORSE_THREAD( ("de-glitch done\n") );
00616
00617
00618 morse_measure_fft_packet_timing(tmp);
00619 DEBUG_MORSE_THREAD( ("fft packet timing done\n") );
00620
00621
00622 morse_calculate_current_wpm(tmp);
00623
00624
00625 update_status(fft_performed, (uint32_t)samples_processed, tmp->words_per_minute);
00626 update_morse_statistics(tmp);
00627 }
00628 pthread_exit(NULL);
00629 }
00630
00631 static void morse_create_fft_amp_array(p_morse_data morse_thread_data)
00632 {
00633 uint32_t i, j, k;
00634 int32_t gsl_error;
00635
00636
00637
00638
00639 k = 0;
00640 for(i = 0; i < audio_device_samples_get(morse_settings.audio_rec_dev); i += morse_settings.tone_packet_size) {
00641
00642 dt_window_apply(morse_thread_data->dt_w, &audio_data[i]);
00643
00644
00645 gsl_error = gsl_fft_real_radix2_transform(&audio_data[i], 1, morse_settings.tone_packet_size);
00646
00647 fft_performed += 1;
00648
00649 for (j = 0; j < morse_settings.tone_packet_size; j++) {
00650 fft_data[j] = sqrt(pow(REAL(&audio_data[i], j), 2.0) + \
00651 pow(IMAG(&audio_data[i], j, morse_settings.tone_packet_size), 2.0));
00652 fft_data[j] *= morse_thread_data->fft_data_gain;
00653 }
00654
00655
00656 morse_thread_data->fft_amp_pa[k] = fft_data[morse_thread_data->cw_frequency];
00657
00658
00659 if(morse_thread_data->apply_agc == MORSE_ENABLE_AGC) {
00660 morse_thread_data->fft_data_gain = agc_update(morse_thread_data->scope_gain, morse_thread_data->fft_amp_pa[k]);
00661 }
00662
00663 DEBUG_TONE_DETECTION( ("%8.2f, %8.2f\n", morse_thread_data->fft_data_gain, morse_thread_data->fft_amp_pa[k]) );
00664 DEBUG_RAW_FFT_ARRAY( ("%10.2f\n", morse_thread_data->fft_amp_pa[k]) );
00665 morse_debug_plot("2", (int32_t)(morse_thread_data->fft_amp_pa[k] / 10.0));
00666 k++;
00667
00668
00669 if(scope_or_fft == PLOT_FFT) {
00670 scope_plot(fft_data, morse_settings.fft_data_size);
00671 }
00672 }
00673 }
00674
00679 static void morse_measure_fft_packet_timing(p_morse_data morse_thread_data)
00680 {
00681 uint32_t i;
00682
00684 for(i = 0; i < morse_settings.fft_amp_pa_size; i++) {
00685
00686 switch(morse_thread_data->state) {
00687 case CW_ON:
00688 if(
00689 (morse_thread_data->fft_amp_pa[i] <= CW_OFF_AMPLITUDE)
00690 && \
00691 (morse_thread_data->cw_on_duration >= morse_settings.minimum_tone_duration)
00692 )
00693 {
00694 morse_thread_data->morse_element.duration = morse_thread_data->cw_on_duration;
00695 duration_2_morse_element(morse_thread_data);
00696 add_element_2_morse_character(morse_thread_data);
00697 morse_thread_data->state = CW_OFF;
00698 morse_thread_data->cw_off_duration = morse_settings.tone_packet_duration;
00699 morse_thread_data->morse_char.char_status |= VALID_CW_ON_TONE_DETECTED;
00700 }
00701 else if(morse_thread_data->fft_amp_pa[i] <= CW_OFF_AMPLITUDE) {
00702
00703
00704
00705
00706
00707 morse_thread_data->cw_off_duration += morse_thread_data->cw_on_duration;
00708 morse_thread_data->state = CW_OFF;
00709 DEBUG_MORSE_TIMING(("noise in cw_off state\n"));
00710 }
00711 else {
00712
00713 morse_thread_data->cw_on_duration += morse_settings.tone_packet_duration;
00714 DEBUG_MORSE_TIMING(("cw_on duration %e seconds\n", morse_thread_data->cw_on_duration));
00715 }
00716 break;
00717
00718 case CW_OFF:
00719 if(
00720 (morse_thread_data->fft_amp_pa[i] >= CW_ON_AMPLITUDE)
00721 && \
00722 (morse_thread_data->cw_off_duration >= morse_settings.minimum_tone_duration)
00723 )
00724 {
00725 morse_thread_data->state = CW_ON;
00726 DEBUG_MORSE_TIMING(("cw_off duration was %e seconds\n", morse_thread_data->cw_off_duration));
00727 morse_thread_data->morse_char.char_status |= VALID_CW_OFF_TONE_DETECTED;
00728 morse_thread_data->cw_on_duration = morse_settings.tone_packet_duration;
00729 }
00730 else if(morse_thread_data->fft_amp_pa[i] >= CW_ON_AMPLITUDE) {
00731
00732
00733
00734
00735
00736 morse_thread_data->cw_on_duration += morse_thread_data->cw_off_duration;
00737 morse_thread_data->state = CW_ON;
00738 DEBUG_MORSE_TIMING(("noise in cw_on state\n"));
00739 }
00740 else {
00741 morse_thread_data->cw_off_duration += morse_settings.tone_packet_duration;
00742 }
00743 break;
00744
00745 default:
00746 morse_thread_data->state = CW_OFF;
00747 break;
00748 }
00749 DEBUG_DGLITCHED_FFT_ARRAY( ("%4.2f, %10.2f\n", morse_thread_data->fft_data_gain, morse_thread_data->fft_amp_pa[i]) );
00750 morse_debug_plot("0", (int32_t)(morse_thread_data->fft_amp_pa[i] / 5.0));
00751 check_for_eoc_eow(morse_thread_data);
00752 }
00753 }
00754
00761 static void morse_calculate_current_wpm(p_morse_data morse_thread_data)
00762 {
00763 if(morse_thread_data->dit_statistics.avg_duration > 0) {
00764
00765 morse_thread_data->words_per_minute = WPM_SECONDS_PER_DIT / morse_thread_data->dit_statistics.avg_duration;
00766 }
00767 else {
00768 morse_thread_data->words_per_minute = 0.0;
00769 }
00770 }
00771
00776 static void check_for_eoc_eow(p_morse_data morse_thread_data)
00777 {
00778 int32_t ctmp, wtmp;
00779
00780 if(morse_thread_data->dit_statistics.avg_duration > 0) {
00781 ctmp = wtmp = 0;
00782 if(morse_thread_data->cw_off_duration >= (CHARACTER_SPACE_DITS * morse_thread_data->dit_statistics.avg_duration)) {
00783 ctmp |= END_OF_CHARACTER;
00784 }
00785 if(morse_thread_data->cw_off_duration >= (WORD_SPACE_DITS * morse_thread_data->dit_statistics.avg_duration)) {
00786 wtmp |= END_OF_WORD;
00787 }
00788
00789 if(
00790 (wtmp & END_OF_WORD)
00791 && \
00792 (!(morse_thread_data->morse_char.word_status & END_OF_WORD))
00793 )
00794 {
00795 morse_thread_data->morse_char.word_status |= END_OF_WORD;
00796 display_character(" ");
00797 DEBUG_DIT_DAH_DETECTION(("EOW\n"));
00798 }
00799 else if(
00800 (ctmp & END_OF_CHARACTER)
00801 && \
00802 !(morse_thread_data->morse_char.char_status & END_OF_CHARACTER)
00803 && \
00804 (morse_thread_data->morse_char.char_status & VALID_CW_ON_TONE_DETECTED)
00805 && \
00806 (morse_thread_data->morse_char.char_status & VALID_CW_OFF_TONE_DETECTED)
00807 )
00808 {
00809 morse_thread_data->morse_char.char_status |= END_OF_CHARACTER;
00810 display_character(morse2txt(&morse_thread_data->morse_char));
00811 reset_morse_char(&morse_thread_data->morse_char);
00812 morse_thread_data->morse_char.word_status = 0;
00813 DEBUG_DIT_DAH_DETECTION(("EOC\n"));
00814 }
00815 else { }
00816 }
00817 }
00818
00823 static void duration_2_morse_element(p_morse_data morse_thread_data)
00824 {
00825 if((morse_thread_data->morse_element.duration >= (SHORT_DAH_VS_DITS * morse_thread_data->morse_element.previous_duration)) &&
00826 (morse_thread_data->morse_element.duration <= (LONG_DAH_VS_DITS * morse_thread_data->morse_element.previous_duration))) {
00827 compute_morse_statistics(&morse_thread_data->dah_statistics, morse_thread_data->cw_on_duration);
00828 }
00829 else if((morse_thread_data->morse_element.duration >= (SHORT_DIT_VS_DAHS * morse_thread_data->morse_element.previous_duration)) &&
00830 (morse_thread_data->morse_element.duration <= (LONG_DIT_VS_DAHS * morse_thread_data->morse_element.previous_duration))) {
00831 compute_morse_statistics(&morse_thread_data->dit_statistics, morse_thread_data->cw_on_duration);
00832 }
00833 else {; }
00834
00835 if((morse_thread_data->morse_element.duration >= (SHORT_DAH_VS_DITS * morse_thread_data->dit_statistics.avg_duration)) &&
00836 (morse_thread_data->morse_element.duration <= (LONG_DAH_VS_DITS * morse_thread_data->dit_statistics.avg_duration))) {
00837 morse_thread_data->morse_element.dah_or_dit = DAH;
00838 DEBUG_DIT_DAH_DETECTION(("dah "));
00839 }
00840 else if((morse_thread_data->morse_element.duration >= (SHORT_DIT_VS_DAHS * morse_thread_data->dah_statistics.avg_duration)) &&
00841 (morse_thread_data->morse_element.duration <= (LONG_DIT_VS_DAHS * morse_thread_data->dah_statistics.avg_duration))) {
00842 morse_thread_data->morse_element.dah_or_dit = DIT;
00843 DEBUG_DIT_DAH_DETECTION(("dit "));
00844 }
00845 else if((morse_thread_data->morse_element.duration >= (SHORT_ELEMENT * morse_thread_data->dah_statistics.avg_duration)) &&
00846 (morse_thread_data->morse_element.duration <= (LONG_ELEMENT * morse_thread_data->dah_statistics.avg_duration))) {
00847 morse_thread_data->morse_element.dah_or_dit = DAH;
00848 DEBUG_DIT_DAH_DETECTION(("dah "));
00849 }
00850 else if((morse_thread_data->morse_element.duration >= (SHORT_ELEMENT * morse_thread_data->dit_statistics.avg_duration)) &&
00851 (morse_thread_data->morse_element.duration <= (LONG_ELEMENT * morse_thread_data->dit_statistics.avg_duration))) {
00852 morse_thread_data->morse_element.dah_or_dit = DIT;
00853 DEBUG_DIT_DAH_DETECTION(("dit "));
00854 }
00855 else {; }
00856 morse_thread_data->morse_element.previous_duration = morse_thread_data->morse_element.duration;
00857 }
00858
00863 static void add_element_2_morse_character(p_morse_data morse_thread_data)
00864 {
00865 if(morse_thread_data->morse_element.dah_or_dit == DAH) {
00866 morse_thread_data->morse_char.dit_dahs |= morse_thread_data->morse_char.element_position_mask;
00867 }
00868 morse_thread_data->morse_char.elements++;
00869 morse_thread_data->morse_char.element_position_mask >>= 1;
00870 }
00871
00876 static void reset_morse_char(p_morse_char morse_char)
00877 {
00878 morse_char->element_position_mask = 0x80;
00879 morse_char->dit_dahs = 0;
00880 morse_char->elements = 0;
00881 morse_char->char_status = CLEAR_CHARACTER_STATUS;
00882 }
00883
00889 static char *morse2txt(p_morse_char morse_char)
00890 {
00891 uint32_t i, j;
00892 char *text = "~";
00893
00894 i = (morse_char->dit_dahs << 8) | morse_char->elements;
00895
00896 for(j = 0; j < MORSE_CODE_DECODE_TABLE_SIZE; j++) {
00897 if(i == MORSE_CODE_DECODE_TABLE[j].morse) {
00898 text = MORSE_CODE_DECODE_TABLE[j].character;
00899 break;
00900 }
00901 }
00902 return text;
00903 }
00904
00910 static void compute_morse_statistics(ts_morse_statistics *dit_dah, double duration)
00911 {
00912 int32_t i;
00913 double sum;
00914
00915 if(dit_dah->samples < MORSE_ELEMENT_HISTORY) {
00916 dit_dah->samples++;
00917 }
00918
00919 for(i = (dit_dah->samples - 1); i > 0; i--) {
00920 dit_dah->duration_history[i] = dit_dah->duration_history[i - 1];
00921 }
00922
00923 dit_dah->duration_history[0] = duration;
00924
00925 sum = 0;
00926 for(i = 0; i < dit_dah->samples; i++) {
00927 sum = sum + dit_dah->duration_history[i];
00928 }
00929
00930 dit_dah->avg_duration = sum / (double)dit_dah->samples;
00931
00932 DEBUG_MORSE_STATISTICS(("samples %d\tsum %f\tavg_duration %f\n", dit_dah->samples, sum, dit_dah->avg_duration));
00933 }
00934
00936 static void morse_recompute_filter(void)
00937 {
00938 double fl, fh;
00939 fl = (morse_settings.fft_freq_step * (double)morse_thread.cw_frequency) - (morse_thread.filter_bandwidth / 2.0);
00940 fh = (morse_settings.fft_freq_step * (double)morse_thread.cw_frequency) + (morse_thread.filter_bandwidth / 2.0);
00941 if (fl <= 0.0) {
00942 fl = 0.0;
00943 }
00944 fwin_change_freq(morse_thread.fir_filter, fl, fh);
00945 }
00946
00947
00948
00949
00950 void morse_set_filter_apply(te_morse_filter_enable apply) { morse_thread.apply_fir = apply; }
00951 void morse_set_agc_apply(te_morse_agc_enable agc_apply) { morse_thread.apply_agc = agc_apply; }
00952 void morse_set_cw_frequency(int32_t freq)
00953 {
00954 morse_thread.cw_frequency = freq;
00955 morse_recompute_filter();
00956 }
00957 void morse_set_filter_bandwidth(int32_t bw)
00958 {
00959 morse_thread.filter_bandwidth = (double)bw;
00960 morse_recompute_filter();
00961 }
00962 int32_t morse_get_filter_bandwidth(void) { return (int32_t)morse_thread.filter_bandwidth; }
00963 void morse_set_filter_taps(uint32_t taps) { morse_thread.filter_taps = taps; }
00964 uint32_t morse_get_filter_taps(void) { return morse_thread.filter_taps; }
00965 uint32_t morse_get_tone_packet_size(void) { return morse_settings.tone_packet_size; }
00966 void morse_set_tone_packet_size(int32_t tone_packet_size) { morse_settings.tone_packet_size = tone_packet_size; }
00967 uint32_t morse_get_fft_data_size(void) { return morse_settings.fft_data_size; }
00968 void morse_set_fft_data_size(uint32_t fft_data_size) { morse_settings.fft_data_size = fft_data_size; }
00969 double morse_get_fft_graph_top(void) { return FFT_GRAPH_TOP; }
00970 p_audio_device morse_audio_rec_dev_get(void) { return morse_settings.audio_rec_dev; }
00971 void morse_audio_sample_rate_set(int32_t Hertz) { morse_settings.sample_frequency = Hertz; }
00976 int32_t morse_audio_sample_rate_get(void) { return morse_settings.sample_frequency; }
00977
00978
00979 int32_t morse_audio_device_type_id_get(void)
00980 {
00981 return morse_settings.audio_type_id;
00982 }
00986 void morse_audio_device_type_id_set(int32_t audio_device_type_id)
00987 {
00988 morse_settings.audio_type_id = audio_device_type_id;
00989 }
00990
00991
00992
00993
00994
00995 te_morse_thread_state morse_get_thread_state(void) { return run; }
00996
00997
00998
00999
01000 te_morse_plot_type morse_get_plot_type(void) { return scope_or_fft; }
01001
01002
01003
01004 void morse_set_plot_type(te_morse_plot_type plt_type) { scope_or_fft = plt_type; }
01005
01006
01007
01008
01009
01010 double morse_max_wpm_supported(double sample_frequency, double packet_length)
01011 {
01012 return (double)(WPM_SECONDS_PER_DIT * sample_frequency / (double)packet_length / (double)MIN_NUM_PACKS_PER_DIT);
01013 }
01014
01015
01016
01017
01018 static void update_status(int32_t ffts, uint32_t samples, double wpm)
01019 {
01020 static int32_t i;
01021
01022 if(++i >= AUDIO_SEGMENTS_PER_STATUS_UPDATE) {
01023 i = 0;
01024 if(user_interface.cb_morse_status != NULL) {
01025 user_interface.cb_morse_status(ffts, samples, wpm);
01026 }
01027 else {
01028 printf("fft's = %d, samples = %d, wpm = %f\n", ffts, samples, wpm);
01029 }
01030 }
01031 }
01032
01033
01034
01035
01036 static void scope_plot(double *vector, uint32_t num_points)
01037 {
01038 if(user_interface.cb_scope_plot != NULL) {
01039 user_interface.cb_scope_plot(vector, num_points);
01040 }
01041 }
01042
01043
01044
01045
01046 static void display_character(char *character)
01047 {
01048 if(user_interface.cb_display_character != NULL) {
01049 user_interface.cb_display_character(character);
01050 }
01051 else {
01052 printf("Decoded character = %s\n", character);
01053 }
01054 }
01055
01056
01057
01058
01059 static void update_morse_statistics(p_morse_data morse_data_p)
01060 {
01061 static int32_t i;
01062 morse_stats_ts morse_stats_s;
01063
01064 morse_stats_s.avg_dah_dur = morse_data_p->dah_statistics.avg_duration;
01065 morse_stats_s.avg_dit_dur = morse_data_p->dit_statistics.avg_duration;
01066 morse_stats_s.wpm = morse_data_p->words_per_minute;
01067 if(0.0 != morse_stats_s.avg_dit_dur) {
01068 morse_stats_s.dah_dit_ratio = morse_stats_s.avg_dah_dur / morse_stats_s.avg_dit_dur;
01069 }
01070
01071 if(++i >= AUDIO_SEGMENTS_PER_STATUS_UPDATE) {
01072 i = 0;
01073 if(NULL != user_interface.cb_Morse_statistics) {
01074 user_interface.cb_Morse_statistics(&morse_stats_s);
01075 }
01076 }
01077 }
01078
01080 void set_update_status_hndlr(UI_update_status hndlr) {user_interface.cb_morse_status = hndlr;}
01082 void set_scope_plot_hndlr(UI_scope_plot hndlr) {user_interface.cb_scope_plot = hndlr;}
01084 void set_display_character_hndlr(UI_display_character hndlr) {user_interface.cb_display_character = hndlr;}
01086 void set_Morse_statistics_display_hndlr(UI_Morse_statistics hndlr) {user_interface.cb_Morse_statistics = hndlr;}
01087
01088 void morse_initialize(void)
01089 {
01090 user_interface.cb_morse_status = NULL;
01091 user_interface.cb_scope_plot = NULL;
01092 user_interface.cb_display_character = NULL;
01093 user_interface.debug_plot = NULL;
01094 user_interface.cb_Morse_statistics = NULL;
01095
01096
01097 morse_settings.audio_rec_dev = audio_create(morse_settings.audio_type_id, AUDIO_DEVICE_RECORD);
01098 }
01099
01100 void morse_shutdown(void)
01101 {
01102 audio_destroy(&morse_settings.audio_rec_dev);
01103 }