From 5907ae25f6e7df86c0e29a1bbc5241bb59b2dee9 Mon Sep 17 00:00:00 2001 From: drowe67 Date: Tue, 5 Aug 2014 04:08:41 +0000 Subject: [PATCH] to freedv interface added the ability to determine demod sync and bit errors, better freedv_rx demo for txt and side info with log file, added switch driver and first pass at SM1000 main() program. Compiles OK but untested git-svn-id: https://svn.code.sf.net/p/freetel/code@1786 01035d8c-6547-0410-b346-abe4f91aad63 --- codec2-dev/README | 51 ++++++---- codec2-dev/src/freedv_api.c | 21 ++--- codec2-dev/src/freedv_api.h | 12 ++- codec2-dev/src/freedv_rx.c | 43 +++++++-- codec2-dev/stm32/Makefile | 36 ++++++- ...leds_switches.h => sm1000_leds_switches.h} | 9 +- ...leds_switches.c => sm1000_leds_switches.c} | 57 +++++++++--- .../stm32/src/sm1000_leds_switches_ut.c | 40 ++++++++ codec2-dev/stm32/src/sm1000_main.c | 93 +++++++++++++++++++ 9 files changed, 302 insertions(+), 60 deletions(-) rename codec2-dev/stm32/inc/{leds_switches.h => sm1000_leds_switches.h} (88%) rename codec2-dev/stm32/src/{leds_switches.c => sm1000_leds_switches.c} (50%) create mode 100644 codec2-dev/stm32/src/sm1000_leds_switches_ut.c create mode 100644 codec2-dev/stm32/src/sm1000_main.c diff --git a/codec2-dev/README b/codec2-dev/README index 0f236e51..97f3c1e6 100644 --- a/codec2-dev/README +++ b/codec2-dev/README @@ -7,12 +7,15 @@ and below. For more information please see: http://rowetel.com/codec2.html Also included is a FDMDV modem (README_fdmdv.txt), and an API for -embeddeding FreeDV in other programs (see example below). For more +embedding FreeDV in other programs (see example below). For more information on building Codec 2 see READE.cmake Quickstart ---------- +NOTE: Use the "codec2" or "codec2-dev" depending on which repository + you are working with + 1/ Listen to Codec 2: $ cd codec2 $ mkdir build_linux @@ -36,11 +39,16 @@ Embedded FreeDV API See freedv_api.h and freedv_api.c, and the demo programs freedv_tx & freedv_rx. Quickstart: - $ ./freedv_tx ../../raw/hts1.raw - | ./freedv_rx - - | play -t raw -r 8000 -s -2 - + $ ./freedv_tx ../../raw/hts1.raw - | ./freedv_rx - - | play -t raw -r 8000 -s -2 -q - Or with a simulated 2 dB SNR channel: - $ ./freedv_tx ../../raw/hts1.raw - | ./fdmdv_channel - - 2 | ./freedv_rx - - | play -t raw -r 8000 -s -2 + $ ./freedv_tx ../../raw/hts1.raw - | ./fdmdv_channel - - 2 | ./freedv_rx - - | play -t raw -r 8000 -s -2 -q - + +To log the demod state information and received text msg characters to a text file: + + $ ./freedv_tx ../../raw/hts1.raw - | ./fdmdv_channel - - 2 | ./freedv_rx - - log.txt | play -t raw -r 8000 -s -2 -q - + $ cat log.txt Programs -------- @@ -61,25 +69,34 @@ switching phase modelling or LSP quantisation on and off. Debugging --------- -1/ For dump file support: +1/ To compile with debug symbols for using gdb: - $ cd codec2 - $ CFLAGS=-DDUMP ./configure - $ make clean && make + $ cd ~/codec2 + $ rm -Rf build_linux && mkdir build_linux + $ cd build_linux + $ CFLAGS=-g cmake .. + $ make -2/ To use gdb: +2/ For dump file support: - $ libtool --mode=execute gdb c2sim + $ cd ~/codec2 + $ rm -Rf build_linux && mkdir build_linux + $ CFLAGS=-DDUMP cmake .. + $ cd build_linux + $ make Directories ----------- - octave - Octave scripts used for visualising internal signals - during development - script - shell scripts for playing and converting raw files - src - C source code - raw - speech files in raw format (16 bits signed linear 8 kHz) - unittest - unit test source code - wav - speech files in wave file format - stm32 - Support for the STM32F4 microcntroller + asterisk & - unmaintained Asterisk support + asterisk-11 + cmake - cmake support files + octave - Octave scripts used for visualising internal signals + during development + script - shell scripts for playing and converting raw files + src - C source code for Codec 2, FDMDV modem, FreeDV API + raw - speech files in raw format (16 bits signed linear 8 kHz) + stm32 - Support for the STM32F4 microcontroller and SM1000 FreeDV speaker-mic + unittest - unit test source code + wav - speech files in wave file format diff --git a/codec2-dev/src/freedv_api.c b/codec2-dev/src/freedv_api.c index 69d2eaa2..4083d597 100644 --- a/codec2-dev/src/freedv_api.c +++ b/codec2-dev/src/freedv_api.c @@ -9,7 +9,7 @@ TODO: [X] speex tx/rx works - [ ] txt messages + [X] txt messages [ ] optional test tx framemode \*---------------------------------------------------------------------------*/ @@ -104,6 +104,7 @@ struct freedv *freedv_open(int mode) { f->freedv_put_next_rx_char = NULL; golay23_init(); + f->total_bit_errors = 0; return f; } @@ -168,7 +169,6 @@ void freedv_tx(struct freedv *f, short mod_out[], short speech_in[]) { // bit/frame to send txt messages data_flag_index = codec2_get_spare_bit_index(f->codec2); - assert(data_flag_index != -1); // not supported for all rates if (f->nvaricode_bits) { f->codec_bits[data_flag_index] = f->tx_varicode_bits[f->varicode_bit_index++]; @@ -266,8 +266,7 @@ int freedv_rx(struct freedv *f, short speech_out[], short demod_in[]) { COMP rx_fdm[FDMDV_MAX_SAMPLES_PER_FRAME]; int bits_per_codec_frame, bytes_per_codec_frame, bits_per_fdmdv_frame; int reliable_sync_bit, i, j, bit, byte, nin_prev, nout; - int recd_codeword, codeword1, data_flag_index, n_ascii, valid; - struct FDMDV_STATS fdmdv_stats; + int recd_codeword, codeword1, data_flag_index, n_ascii; short abit[1]; char ascii_out; @@ -282,9 +281,9 @@ int freedv_rx(struct freedv *f, short speech_out[], short demod_in[]) { nin_prev = f->nin; fdmdv_demod(f->fdmdv, f->fdmdv_bits, &reliable_sync_bit, rx_fdm, &f->nin); - fdmdv_get_demod_stats(f->fdmdv, &fdmdv_stats); + fdmdv_get_demod_stats(f->fdmdv, &f->fdmdv_stats); - if (fdmdv_stats.sync) { + if (f->fdmdv_stats.sync) { if (reliable_sync_bit == 0) { memcpy(f->rx_bits, f->fdmdv_bits, bits_per_fdmdv_frame*sizeof(int)); nout = 0; @@ -307,6 +306,8 @@ int freedv_rx(struct freedv *f, short speech_out[], short demod_in[]) { recd_codeword |= f->rx_bits[i]; } codeword1 = golay23_decode(recd_codeword); + f->total_bit_errors += golay23_count_errors(recd_codeword, codeword1); + //codeword1 = recd_codeword; //fprintf(stderr, "received codeword1: 0x%x decoded codeword1: 0x%x\n", recd_codeword, codeword1); @@ -324,20 +325,16 @@ int freedv_rx(struct freedv *f, short speech_out[], short demod_in[]) { // extract txt msg data bit ------------------------------------------------------------ data_flag_index = codec2_get_spare_bit_index(f->codec2); - assert(data_flag_index != -1); // not supported for all rates - abit[0] = f->codec_bits[data_flag_index]; n_ascii = varicode_decode(&f->varicode_dec_states, &ascii_out, abit, 1, 1); - assert((n_ascii == 0) || (n_asacii == 1)); if (n_ascii && (f->freedv_put_next_rx_char != NULL)) { (*f->freedv_put_next_rx_char)(f->callback_state, ascii_out); } // reconstruct missing bit we steal for data bit and decode speech - valid = codec2_rebuild_spare_bit(f->codec2, f->codec_bits); - assert(valid != -1); + codec2_rebuild_spare_bit(f->codec2, f->codec_bits); // pack bits, MSB received first @@ -357,7 +354,7 @@ int freedv_rx(struct freedv *f, short speech_out[], short demod_in[]) { /* squelch if beneath SNR threshold */ - if (fdmdv_stats.snr_est < f->snr_thresh) { + if (f->fdmdv_stats.snr_est < f->snr_thresh) { for(i=0; iftxt != NULL) { + fprintf(pstate->ftxt, "text char received callback: %c\n", c); + } } int main(int argc, char *argv[]) { - FILE *fin, *fout; - short speech_out[FREEDV_NSAMPLES]; - short demod_in[FREEDV_NSAMPLES]; - struct freedv *freedv; - int nin, nout; + FILE *fin, *fout, *ftxt; + short speech_out[FREEDV_NSAMPLES]; + short demod_in[FREEDV_NSAMPLES]; + struct freedv *freedv; + int nin, nout, frame; + struct my_callback_state my_cb_state; if (argc < 3) { - printf("usage: %s InputModemSpeechFile OutputSpeechawFile\n", argv[0]); - printf("e.g %s hts1a_fdmdv.raw hts1a_out.raw\n", argv[0]); + printf("usage: %s InputModemSpeechFile OutputSpeechawFile txtLogFile\n", argv[0]); + printf("e.g %s hts1a_fdmdv.raw hts1a_out.raw txtLogFile\n", argv[0]); exit(1); } @@ -66,9 +74,20 @@ int main(int argc, char *argv[]) { exit(1); } + ftxt = NULL; + if ( (argc > 3) && (strcmp(argv[3],"|") != 0) ) { + if ((ftxt = fopen(argv[3],"wt")) == NULL ) { + fprintf(stderr, "Error opening txt Log File: %s: %s.\n", + argv[3], strerror(errno)); + exit(1); + } + } + freedv = freedv_open(FREEDV_MODE_1600); assert(freedv != NULL); + my_cb_state.ftxt = ftxt; + freedv->callback_state = (void*)&my_cb_state; freedv->freedv_put_next_rx_char = &my_put_next_rx_char; /* Note we need to work out how many samples demod needs on each @@ -82,6 +101,14 @@ int main(int argc, char *argv[]) { fwrite(speech_out, sizeof(short), nout, fout); nin = freedv_nin(freedv); + /* log some side info to the txt file */ + + frame++; + if (ftxt != NULL) { + fprintf(ftxt, "frame: %d demod sync: %d demod snr: %3.2f dB bit errors: %d\n", frame, + freedv->fdmdv_stats.sync, freedv->fdmdv_stats.snr_est, freedv->total_bit_errors); + } + /* if this is in a pipeline, we probably don't want the usual buffering to occur */ diff --git a/codec2-dev/stm32/Makefile b/codec2-dev/stm32/Makefile index 89386cc5..edc5427f 100644 --- a/codec2-dev/stm32/Makefile +++ b/codec2-dev/stm32/Makefile @@ -64,9 +64,12 @@ $(CODEC2_SRC)/codebookd.c \ $(CODEC2_SRC)/codebookjvm.c \ $(CODEC2_SRC)/codebookge.c \ $(CODEC2_SRC)/dump.c \ -$(CODEC2_SRC)/fdmdv.c +$(CODEC2_SRC)/fdmdv.c \ +$(CODEC2_SRC)/freedv_api.c \ +$(CODEC2_SRC)/varicode.c \ +$(CODEC2_SRC)/golay23.c -CFLAGS += -D__EMBEDDED__ -DTIMER +CFLAGS += -D__EMBEDDED__ #enable this for dump files to help verify optimisation #CFLAGS += -DDUMP @@ -106,7 +109,7 @@ OBJS = $(SRCS:.c=.o) ################################################### -all: libstm32f4.a codec2_profile.elf fft_test.elf dac_ut.elf dac_play.elf adc_rec.elf pwm_ut.elf power_ut.elf fdmdv_profile.elf +all: libstm32f4.a codec2_profile.elf fft_test.elf dac_ut.elf dac_play.elf adc_rec.elf pwm_ut.elf power_ut.elf fdmdv_profile.elf sm1000_leds_switches_ut.elf sm1000.elf dl/$(PERIPHLIBZIP): mkdir -p dl @@ -134,7 +137,7 @@ src/system_stm32f4xx.c CODEC2_PROFILE_SRCS += $(CODEC2_SRCS) codec2_profile.elf: $(CODEC2_PROFILE_SRCS) - $(CC) $(CFLAGS) $^ -o $@ $(LIBPATHS) $(LIBS) + $(CC) $(CFLAGS) -DTIMER $^ -o $@ $(LIBPATHS) $(LIBS) fft_test.elf: $(FFT_TEST_SRCS) $(CC) $(CFLAGS) $^ -o $@ $(LIBPATHS) $(LIBS) @@ -211,6 +214,31 @@ src/stm32f4_timer.c FDMDV_PROFILE_SRCS += $(CODEC2_SRCS) fdmdv_profile.elf: $(FDMDV_PROFILE_SRCS) + $(CC) $(CFLAGS) -DTIMER $^ -o $@ $(LIBPATHS) $(LIBS) + +SM1000_LEDS_SWITCHES_UT_SRCS=\ +src/sm1000_leds_switches_ut.c \ +src/sm1000_leds_switches.c \ +src/system_stm32f4xx.c \ +src/startup_stm32f4xx.s \ +src/init.c + +sm1000_leds_switches_ut.elf: $(SM1000_LEDS_SWITCHES_UT_SRCS) + $(CC) $(CFLAGS) $^ -o $@ $(LIBPATHS) $(LIBS) + +SM1000_SRCS=\ +src/sm1000_main.c \ +src/sm1000_leds_switches.c \ +../src/fifo.c \ +src/stm32f4_adc.c \ +src/stm32f4_dac.c \ +src/system_stm32f4xx.c \ +src/startup_stm32f4xx.s \ +src/init.c + +SM1000_SRCS += $(CODEC2_SRCS) + +sm1000.elf: $(SM1000_SRCS) $(CC) $(CFLAGS) $^ -o $@ $(LIBPATHS) $(LIBS) clean: diff --git a/codec2-dev/stm32/inc/leds_switches.h b/codec2-dev/stm32/inc/sm1000_leds_switches.h similarity index 88% rename from codec2-dev/stm32/inc/leds_switches.h rename to codec2-dev/stm32/inc/sm1000_leds_switches.h index 98ddbdd9..b6207d03 100644 --- a/codec2-dev/stm32/inc/leds_switches.h +++ b/codec2-dev/stm32/inc/sm1000_leds_switches.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ - FILE........: leds_switches.h + FILE........: sm1000_leds_switches.h AUTHOR......: David Rowe DATE CREATED: 18 July 2014 @@ -28,10 +28,15 @@ #ifndef __LEDS_SWITCHES__ #define __LEDS_SWITCHES__ -void leds_switches_init(void); +void sm1000_leds_switches_init(void); + void led_pwr(int state); void led_ptt(int state); void led_rt(int state); void led_err(int state); +int switch_ptt(void); +int switch_select(void); +int switch_back(void); + #endif diff --git a/codec2-dev/stm32/src/leds_switches.c b/codec2-dev/stm32/src/sm1000_leds_switches.c similarity index 50% rename from codec2-dev/stm32/src/leds_switches.c rename to codec2-dev/stm32/src/sm1000_leds_switches.c index 3985b839..b37a7a90 100644 --- a/codec2-dev/stm32/src/leds_switches.c +++ b/codec2-dev/stm32/src/sm1000_leds_switches.c @@ -1,10 +1,10 @@ /*---------------------------------------------------------------------------*\ - FILE........: leds_switches.c + FILE........: sm1000_leds_switches.c AUTHOR......: David Rowe DATE CREATED: 18 July 2014 - Functions for controlling LEDs and reading switches on SM1000. + Functions for controlling LEDs and reading switches on the SM1000. \*---------------------------------------------------------------------------*/ @@ -25,22 +25,40 @@ along with this program; if not, see . */ -#define LED_PWR 12 -#define LED_PTT 13 -#define LED_RT 14 -#define LED_ERR 15 +#define LED_PWR 12 +#define LED_PTT 13 +#define LED_RT 14 +#define LED_ERR 15 +#define SWITCH_PTT 7 +#define SWITCH_SELECT 0 +#define SWITCH_BACK 1 -#include "stm32f4xx_conf.h" -#include "stm32f4xx.h" -#include "leds_switches.h" +#include +#include +#include "sm1000_leds_switches.h" -void leds_switches_init(void) { - RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN; // enable the clock to GPIOD +void sm1000_leds_switches_init(void) { + GPIO_InitTypeDef GPIO_InitStruct; - // Set pins as general purpose IOs + RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE); - GPIOD->MODER = (2 << LED_PWR) | (2 << LED_PTT) | (2 << LED_RT) | (2 << LED_ERR); -} + /* output pins */ + + GPIO_InitStruct.GPIO_Pin = LED_PWR | LED_PTT | LED_RT | LED_ERR; + GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT; + GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; + GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; + GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL; + GPIO_Init(GPIOD, &GPIO_InitStruct); + + /* input pins */ + + GPIO_InitStruct.GPIO_Pin = SWITCH_PTT | SWITCH_SELECT | SWITCH_BACK; + GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN; + GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; + GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL; /* we have our own external pull ups */ + GPIO_Init(GPIOD, &GPIO_InitStruct); + } void led_pwr(int state) { if (state) @@ -70,3 +88,14 @@ void led_err(int state) { GPIOD->ODR &= ~(1 << LED_ERR); } +int switch_ptt(void) { + return GPIOA->IDR & SWITCH_PTT; +} + +int switch_select(void) { + return GPIOA->IDR & SWITCH_SELECT; +} + +int switch_back(void) { + return GPIOA->IDR & SWITCH_BACK; +} diff --git a/codec2-dev/stm32/src/sm1000_leds_switches_ut.c b/codec2-dev/stm32/src/sm1000_leds_switches_ut.c new file mode 100644 index 00000000..32813ff5 --- /dev/null +++ b/codec2-dev/stm32/src/sm1000_leds_switches_ut.c @@ -0,0 +1,40 @@ +/*---------------------------------------------------------------------------*\ + + FILE........: sm1000_leds_switches_ut.c + AUTHOR......: David Rowe + DATE CREATED: August 5 2014 + + Unit Test program for the SM1000 switches and LEDs driver. + +\*---------------------------------------------------------------------------*/ + +/* + Copyright (C) 2014 David Rowe + + All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 2.1, as + published by the Free Software Foundation. This program is + distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, see . +*/ + +#include +#include "sm1000_leds_switches.h" + +int main(void) { + sm1000_leds_switches_init(); + while(1) { + led_pwr(switch_select()); + led_ptt(switch_ptt()); + led_rt(switch_back()); + led_err(!switch_back()); + } +} + diff --git a/codec2-dev/stm32/src/sm1000_main.c b/codec2-dev/stm32/src/sm1000_main.c new file mode 100644 index 00000000..90c926c8 --- /dev/null +++ b/codec2-dev/stm32/src/sm1000_main.c @@ -0,0 +1,93 @@ +/*---------------------------------------------------------------------------*\ + + FILE........: sm1000_main.c + AUTHOR......: David Rowe + DATE CREATED: August 5 2014 + + Main program for SM1000. + +\*---------------------------------------------------------------------------*/ + +/* + Copyright (C) 2014 David Rowe + + All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 2.1, as + published by the Free Software Foundation. This program is + distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, see . +*/ + +#include "stm32f4_adc.h" +#include "stm32f4_dac.h" +#include "freedv_api.h" +#include "sm1000_leds_switches.h" + +int main(void) { + struct freedv *f; + short buf[FREEDV_NSAMPLES]; + int nin, nout; + + /* init all the drivers for various peripherals */ + + sm1000_leds_switches_init(); + dac_open(); + adc_open(); + f = freedv_open(FREEDV_MODE_1600); + + /* LEDs into a known state */ + + led_pwr(1); led_ptt(0); led_rt(0); led_err(0); + + /* + TODO: + [ ] UT analog interfaces from file IO + [ ] UTs for simultaneous tx & rx on analog interfaces + [ ] measure CPU load of various parts with a blinky + [ ] detect program assert type errors with a blinky + [ ] timer tick function to measure 10ms-ish type times + [ ] switch debouncing? + [ ] light led with bit errors + */ + + while(1) { + + if(switch_ptt()) { + + /* Transmit -------------------------------------------------------------------------*/ + + /* ADC2 is the SM1000 microphone, DAC1 is the modulator signal we send to radio tx */ + + if (adc2_read(buf, FREEDV_NSAMPLES) == FREEDV_NSAMPLES) { + freedv_tx(f, buf, buf); + dac1_write(buf, FREEDV_NSAMPLES); + led_ptt(1); led_rt(0); led_err(0); + } + } + else { + + /* Receive --------------------------------------------------------------------------*/ + + /* ADC1 is the demod in signal from the radio rx, DAC2 is the SM1000 speaker */ + + nin = freedv_nin(f); + f->total_bit_errors = 0; + + if (adc1_read(buf, nin) == nin) { + nout = freedv_rx(f, buf, buf); + dac2_write(buf, nout); + led_ptt(0); led_rt(f->fdmdv_stats.sync); led_err(f->total_bit_errors); + } + + } + + } /* while(1) ... */ +} + -- 2.25.1