diff options
author | Maximilien Cuony <maximilien@theglu.org> | 2016-06-04 00:24:30 +0200 |
---|---|---|
committer | Maximilien Cuony <maximilien@theglu.org> | 2016-06-04 00:24:30 +0200 |
commit | f4a93ecce5813c9ddf9e5dc4a416777681dc04f9 (patch) | |
tree | 20f9a258417da24fcabb62819f76bf81e60f0834 /src/common | |
parent | 53c2afbf719d91f660e815c160e73066ef1ec9e0 (diff) | |
download | glutte-o-matic-f4a93ecce5813c9ddf9e5dc4a416777681dc04f9.tar.gz glutte-o-matic-f4a93ecce5813c9ddf9e5dc4a416777681dc04f9.tar.bz2 glutte-o-matic-f4a93ecce5813c9ddf9e5dc4a416777681dc04f9.zip |
Simulator: GPS, Audio, CW
Diffstat (limited to 'src/common')
-rw-r--r-- | src/common/includes/Audio/audio.h | 50 | ||||
-rw-r--r-- | src/common/includes/Audio/cw.h | 49 | ||||
-rw-r--r-- | src/common/includes/GPIO/i2c.h | 53 | ||||
-rw-r--r-- | src/common/src/Audio/audio.c | 32 | ||||
-rw-r--r-- | src/common/src/Audio/cw.c | 605 | ||||
-rw-r--r-- | src/common/src/Core/common.c | 9 | ||||
-rw-r--r-- | src/common/src/Core/main.c | 106 |
7 files changed, 847 insertions, 57 deletions
diff --git a/src/common/includes/Audio/audio.h b/src/common/includes/Audio/audio.h new file mode 100644 index 0000000..006f82e --- /dev/null +++ b/src/common/includes/Audio/audio.h @@ -0,0 +1,50 @@ +#ifndef __AUDIO_H__ +#define __AUDIO_H__ + +#include <stdint.h> +#include <stdbool.h> + +typedef void AudioCallbackFunction(void *context,int buffer); + +#define Audio8000HzSettings 256,5,12,1,8000 +#define Audio16000HzSettings 213,2,13,0,16000 +#define Audio32000HzSettings 213,2,6,1,32000 +#define Audio48000HzSettings 258,3,3,1,48000 +#define Audio96000HzSettings 344,2,3,1,96000 +#define Audio22050HzSettings 429,4,9,1,22050 +#define Audio44100HzSettings 271,2,6,0,44100 +#define AudioVGAHSyncSettings 419,2,13,0,31475 // 31475.3606. Actual VGA timer is 31472.4616. + +#define AUDIO_BUF_LEN 4096 + + +// Initialize and power up audio hardware. Use the above defines for the parameters. +// Can probably only be called once. +void audio_initialize(int plln,int pllr,int i2sdiv,int i2sodd, int rate); + +// Power up and down the audio hardware. +void audio_on(); +void audio_off(); + +// Set audio volume in steps of 0.5 dB. 0xff is +12 dB. +void audio_set_volume(int volume); + +// Output one audio sample directly to the hardware without using DMA. +void audio_output_sample(int16_t sample); +void audio_output_sample_without_blocking(int16_t sample); + +// Start and stop audio playback using DMA. +// Callback is optional, and called whenever a new buffer is needed. +void audio_play_with_callback(AudioCallbackFunction *callback,void *context); +void audio_stop(); + +// Provide a new buffer to the audio DMA. Output is double buffered, so +// at least two buffers must be maintained by the program. It is not allowed +// to overwrite the previously provided buffer until after the next callback +// invocation. +// Buffers must reside in DMA1-accessible memory, that is, the 128k RAM bank, +// or flash. +void audio_provide_buffer(void *samples,int numsamples); +bool audio_provide_buffer_without_blocking(void *samples,int numsamples); + +#endif diff --git a/src/common/includes/Audio/cw.h b/src/common/includes/Audio/cw.h new file mode 100644 index 0000000..384918d --- /dev/null +++ b/src/common/includes/Audio/cw.h @@ -0,0 +1,49 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2015 Matthias P. Braendli + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. +*/ + +#ifndef __CW_H_ +#define __CW_H_ + +#include <stdint.h> +#include <stddef.h> + +// Setup the CW generator to create audio samples at the given +// samplerate. +void cw_psk31_init(unsigned int samplerate); + +// Append new CW or PSK31 text to transmit +// CW/PSK31 audio centre frequency in Hz +// if dit_duration == 0, message is sent in PSK31 +// otherwise it is sent in CW, with dit_duration in ms +// returns 0 on failure, 1 on success +int cw_psk31_push_message(const char* text, int frequency, int dit_duration); + +// Write the waveform into the buffer (stereo), both for cw and psk31 +size_t cw_psk31_fill_buffer(int16_t *buf, size_t bufsize); + +// Return 1 if the CW or PSK31 generator is running +int cw_psk31_busy(void); + +#endif // __CW_H_ + diff --git a/src/common/includes/GPIO/i2c.h b/src/common/includes/GPIO/i2c.h new file mode 100644 index 0000000..109213b --- /dev/null +++ b/src/common/includes/GPIO/i2c.h @@ -0,0 +1,53 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2015 Matthias P. Braendli + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. +*/ + +#ifndef __I2C_H_ +#define __I2C_H_ + +#include <stdint.h> + +/* Initialise I2C on the board for both the audio codec and the GPS receiver */ +void i2c_init(); + +/* Do an I2C write, return 1 on success, 0 on failure */ +int i2c_write(uint8_t device, const uint8_t *txbuf, int len); + +/* Do an I2C read into rxbuf. + * Returns number of bytes received, or 0 in case of failure + */ +int i2c_read(uint8_t device, uint8_t *rxbuf, int len); + +/* Do an I2C write for the address, and then a read into rxbuf. + * Returns number of bytes received, or 0 in case of failure + */ +int i2c_read_from(uint8_t device, uint8_t address, uint8_t *rxbuf, int len); + +/* Start an I2C transaction, keeping exclusive access to I2C */ +void i2c_transaction_start(void); + +/* End an I2C transaction, unlocking the exclusive access to I2C */ +void i2c_transaction_end(void); + +#endif // __I2C_H_ + diff --git a/src/common/src/Audio/audio.c b/src/common/src/Audio/audio.c new file mode 100644 index 0000000..3d3dbf4 --- /dev/null +++ b/src/common/src/Audio/audio.c @@ -0,0 +1,32 @@ +#include "Audio/audio.h" + +#include <stdlib.h> + +static void audio_write_register(uint8_t address, uint8_t value); +static void audio_start_dma_and_request_buffers(); +static void audio_stop_dma(); + +static AudioCallbackFunction *callback_function; +static void *callback_context; +static int16_t * volatile next_buffer_samples; +static volatile int next_buffer_length; +static volatile int buffer_number; +static volatile bool dma_running; + +void audio_initialize_platform(int plln, int pllr, int i2sdiv, int i2sodd, int rate); + +void audio_initialize(int plln, int pllr, int i2sdiv, int i2sodd, int rate) { + + // Intitialize state. + callback_function = NULL; + callback_context = NULL; + next_buffer_samples = NULL; + next_buffer_length = 0; + buffer_number = 0; + dma_running = false; + + audio_initialize_platform(plln, pllr, i2sdiv, i2sodd, rate); + + audio_set_volume(0xff); + +} diff --git a/src/common/src/Audio/cw.c b/src/common/src/Audio/cw.c new file mode 100644 index 0000000..3f3b97d --- /dev/null +++ b/src/common/src/Audio/cw.c @@ -0,0 +1,605 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016 Matthias P. Braendli + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. +*/ + +/* CW and PSK31 generator + * + * Concept: + * + * +-------------------+ +----------------+ + * | cw_push_message() | -> cw_msg_queue -> | cw_psk31task() | -> cw_audio_queue + * +-------------------+ +----------------+ + * + * The cw_psk31_fill_buffer() function can be called to fetch audio from the + * audio_queue + */ + +#include "Audio/cw.h" +#include "Core/common.h" +#include "Audio/audio.h" + +#ifdef SIMULATOR +#define arm_cos_f32 cosf +#define arm_sin_f32 sinf +#else +#include "arm_math.h" +#endif + +/* Kernel includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" +#include "semphr.h" + +#define MAX_MESSAGE_LEN 1024 +#define MAX_ON_BUFFER_LEN 8192 + +const uint8_t cw_mapping[60] = { // {{{ + // Read bits from right to left + + 0b110101, //+ ASCII 43 + 0b110101, //, ASCII 44 + 0b1011110, //- ASCII 45 + + 0b1010101, //., ASCII 46 + 0b110110, // / ASCII 47 + + 0b100000, // 0, ASCII 48 + 0b100001, // 1 + 0b100011, + 0b100111, + 0b101111, + 0b111111, + 0b111110, + 0b111100, + 0b111000, + 0b110000, // 9, ASCII 57 + + // The following are mostly invalid, but + // required to fill the gap in ASCII between + // numerals and capital letters + 0b10, // : + 0b10, // ; + 0b10, // < + 0b10, // = + 0b10, // > + 0b1110011, // ? + 0b1101001, //@ + + 0b101, // A ASCII 65 + 0b11110, + 0b11010, + 0b1110, + 0b11, + 0b11011, + 0b1100, + 0b11111, + 0b111, + 0b10001, + 0b1010, + 0b11101, + 0b100, //M + 0b110, + 0b1000, + 0b11001, + 0b10100, + 0b1101, + 0b1111, + 0b10, + 0b1011, + 0b10111, + 0b1001, + 0b10110, + 0b10010, + 0b11100, // Z + + 0b101010, //Start, ASCII [ + 0b1010111, // SK , ASCII '\' +}; //}}} + +#if ENABLE_PSK31 +/* + * PSK31 Varicode + * http://aintel.bi.ehu.es/psk31.html + */ +const char *psk31_varicode[] = { // {{{ + "1010101011", + "1011011011", + "1011101101", + "1101110111", + "1011101011", + "1101011111", + "1011101111", + "1011111101", + "1011111111", + "11101111", + "11101", + "1101101111", + "1011011101", + "11111", + "1101110101", + "1110101011", + "1011110111", + "1011110101", + "1110101101", + "1110101111", + "1101011011", + "1101101011", + "1101101101", + "1101010111", + "1101111011", + "1101111101", + "1110110111", + "1101010101", + "1101011101", + "1110111011", + "1011111011", + "1101111111", + "1", + "111111111", + "101011111", + "111110101", + "111011011", + "1011010101", + "1010111011", + "101111111", + "11111011", + "11110111", + "101101111", + "111011111", + "1110101", + "110101", + "1010111", + "110101111", + "10110111", + "10111101", + "11101101", + "11111111", + "101110111", + "101011011", + "101101011", + "110101101", + "110101011", + "110110111", + "11110101", + "110111101", + "111101101", + "1010101", + "111010111", + "1010101111", + "1010111101", + "1111101", + "11101011", + "10101101", + "10110101", + "1110111", + "11011011", + "11111101", + "101010101", + "1111111", + "111111101", + "101111101", + "11010111", + "10111011", + "11011101", + "10101011", + "11010101", + "111011101", + "10101111", + "1101111", + "1101101", + "101010111", + "110110101", + "101011101", + "101110101", + "101111011", + "1010101101", + "111110111", + "111101111", + "111111011", + "1010111111", + "101101101", + "1011011111", + "1011", + "1011111", + "101111", + "101101", + "11", + "111101", + "1011011", + "101011", + "1101", + "111101011", + "10111111", + "11011", + "111011", + "1111", + "111", + "111111", + "110111111", + "10101", + "10111", + "101", + "110111", + "1111011", + "1101011", + "11011111", + "1011101", + "111010101", + "1010110111", + "110111011", + "1010110101", + "1011010111", + "1110110101", +}; //}}} +#endif + +struct cw_message_s { + char message[MAX_MESSAGE_LEN]; + size_t message_len; + + int freq; + + // If dit_duration is 0, the message is sent in PSK31 + int dit_duration; +}; + +// The queue contains above structs +QueueHandle_t cw_msg_queue; + +// Queue that contains audio data +QueueHandle_t cw_audio_queue; +static int cw_psk31_samplerate; + +static int cw_transmit_ongoing; + +static void cw_psk31_task(void *pvParameters); + +void cw_psk31_init(unsigned int samplerate) +{ + cw_psk31_samplerate = samplerate; + cw_transmit_ongoing = 0; + + cw_msg_queue = xQueueCreate(10, sizeof(struct cw_message_s)); + if (cw_msg_queue == 0) { + while(1); /* fatal error */ + } + + cw_audio_queue = xQueueCreate(1, AUDIO_BUF_LEN * sizeof(int16_t)); + if (cw_audio_queue == 0) { + while(1); /* fatal error */ + } + + xTaskCreate( + cw_psk31_task, + "CWPSKTask", + 8*configMINIMAL_STACK_SIZE, + (void*) NULL, + tskIDLE_PRIORITY + 3UL, + NULL); +} + +/* Parse one CW letter/symbol, and fill in the on_buffer. + * Returns number of uint8_t written. + */ +size_t cw_symbol(uint8_t sym, uint8_t *on_buffer, size_t on_buffer_size) +{ + uint8_t p = 0; + uint8_t val = cw_mapping[sym]; + size_t pos = 0; + + while((val >> p) != 0b1) { + + if (((val >> p) & 0b1) == 0b1) { + if (pos + 2 < on_buffer_size) { + // tone(1) + on_buffer[pos++] = 1; + + // silence(1) + on_buffer[pos++] = 0; + } + } + else { + if (pos + 4 < on_buffer_size) { + // tone(3) + on_buffer[pos++] = 1; + on_buffer[pos++] = 1; + on_buffer[pos++] = 1; + + // silence(1) + on_buffer[pos++] = 0; + } + } + + p++; + } + + // silence(2) + if (pos + 2 < on_buffer_size) { + for (int i = 0; i < 2; i++) { + on_buffer[pos++] = 0; + } + } + + return pos; +} + +// Transmit a string in morse code or PSK31. +// Supported range for CW: +// All ASCII between '+' and '\', which includes +// numerals and capital letters. +// Distinction between CW and PSK31 is done on dit_duration==0 +int cw_psk31_push_message(const char* text, int dit_duration, int frequency) +{ + const int text_len = strlen(text); + + if (strlen(text) > MAX_MESSAGE_LEN) { + return 0; + } + + struct cw_message_s msg; + for (int i = 0; i < MAX_MESSAGE_LEN; i++) { + if (i < text_len) { + msg.message[i] = text[i]; + } + else { + msg.message[i] = '\0'; + } + } + msg.message_len = text_len; + msg.freq = frequency; + msg.dit_duration = dit_duration; + + xQueueSendToBack(cw_msg_queue, &msg, portMAX_DELAY); /* Send Message */ + + cw_message_sent(msg.message); + + return 1; +} + +/* Parse the message and fill the on_buffer with CW on/CW off information. + * Returns the number of on/off bits written. + */ +static size_t cw_text_to_on_buffer(const char *msg, uint8_t *on_buffer, size_t on_buffer_size) +{ + size_t pos = 0; + const char* sym = msg; + do { + if (*sym < '+' || *sym > '\\') { + if (pos + 3 < on_buffer_size) { + for (int i = 0; i < 3; i++) { + on_buffer[pos++] = 0; + } + } + } + else { + pos += cw_symbol(*sym - '+', on_buffer + pos, on_buffer_size - pos); + } + sym++; + } while (*sym != '\0'); + + return pos; +} + + +#if ENABLE_PSK31 +/* + * Turn a null terminated ASCII string into a uint8_t buffer + * of 0 and 1 representing the PSK31 varicode for the input. + * + * outstr must be at least size 20 + strlen(instr)*12 + 20 to accomodate + * the header and tail. + * + * Returns number of bytes written. + */ +static size_t psk31_text_to_phase_buffer(const char* instr, uint8_t* outbits) +{ + int i=0, j, k; + + /* Header of 0s */ + for (j=0; j < 20; j++) { + outbits[i++] = 0; + } + + /* Encode the message, with 00 between letters */ + for (j=0; j < strlen(instr); j++) { + const char* varicode_bits = psk31_varicode[(int)instr[j]]; + for(k=0; k < strlen(varicode_bits); k++) { + outbits[i++] = (varicode_bits[k] == '1') ? 1 : 0; + } + outbits[i++] = 0; + outbits[i++] = 0; + } + + /* Tail of 0s */ + for (j=0; j < 20; j++) { + outbits[i++] = 0; + } + + return i; +} +#endif + + +size_t cw_psk31_fill_buffer(int16_t *buf, size_t bufsize) +{ + if (xQueueReceiveFromISR(cw_audio_queue, buf, NULL)) { + return bufsize; + } + else { + return 0; + } +} + +static int16_t cw_audio_buf[AUDIO_BUF_LEN]; +static uint8_t cw_psk31_buffer[MAX_ON_BUFFER_LEN]; +static struct cw_message_s cw_fill_msg_current; + +// Routine to generate CW audio +static float cw_generate_audio_ampl = 0.0f; +static float cw_generate_audio_nco = 0.0f; +static int16_t cw_generate_audio(float omega, int i, int t) +{ + int16_t s = 0; + // Remove clicks from CW + if (cw_psk31_buffer[i]) { + const float remaining = 32768.0f - cw_generate_audio_ampl; + cw_generate_audio_ampl += remaining / 64.0f; + } + else { + cw_generate_audio_ampl -= cw_generate_audio_ampl / 64.0f; + + if (cw_generate_audio_ampl < 0) { + cw_generate_audio_ampl = 0; + } + } + + cw_generate_audio_nco += omega; + if (cw_generate_audio_nco > FLOAT_PI) { + cw_generate_audio_nco -= 2.0f * FLOAT_PI; + } + + s = cw_generate_audio_ampl * arm_sin_f32(cw_generate_audio_nco); + return s; +} + +#if ENABLE_PSK31 +static float psk31_generate_audio_nco = 0.0f; +static int psk31_current_psk_phase = 1; +static int16_t psk31_generate_audio(float omega, int i, int t, int samples_per_symbol) +{ + int16_t s = 0; + const float base_ampl = 20000.0f; + float psk31_generate_audio_ampl = 0.0f; + + if (cw_psk31_buffer[i] == 1) { + psk31_generate_audio_ampl = base_ampl; + } + else { + psk31_generate_audio_ampl = base_ampl * arm_cos_f32( + FLOAT_PI*(float)t/(float)samples_per_symbol); + } + + psk31_generate_audio_nco += omega; + if (psk31_generate_audio_nco > FLOAT_PI) { + psk31_generate_audio_nco -= 2.0f * FLOAT_PI; + } + + s = psk31_generate_audio_ampl * + arm_sin_f32(psk31_generate_audio_nco + + (psk31_current_psk_phase == 1 ? 0.0f : FLOAT_PI)); + + return s; +} +#endif + +static void cw_psk31_task(void *pvParameters) +{ + int buf_pos = 0; + + while (1) { + int status = xQueueReceive(cw_msg_queue, &cw_fill_msg_current, portMAX_DELAY); + if (status == pdTRUE) { + + size_t cw_psk31_buffer_len = 0; + + cw_transmit_ongoing = 1; + + // Audio should be off, turn it on + audio_on(); + + if (cw_fill_msg_current.dit_duration) { + cw_psk31_buffer_len = cw_text_to_on_buffer( + cw_fill_msg_current.message, + cw_psk31_buffer, + MAX_ON_BUFFER_LEN); + + } +#if ENABLE_PSK31 + else { + cw_psk31_buffer_len = psk31_text_to_phase_buffer( + cw_fill_msg_current.message, + cw_psk31_buffer); + } +#endif + + // Angular frequency of NCO + const float omega = 2.0f * FLOAT_PI * cw_fill_msg_current.freq / + (float)cw_psk31_samplerate; + + const int samples_per_symbol = (cw_fill_msg_current.dit_duration != 0) ? + (cw_psk31_samplerate * cw_fill_msg_current.dit_duration) / 1000 : + /* BPSK31 is at 31.25 symbols per second. */ + cw_psk31_samplerate * 100 / 3125; + +#if ENABLE_PSK31 + psk31_current_psk_phase = 1; +#endif + + for (int i = 0; i < cw_psk31_buffer_len; i++) { + for (int t = 0; t < samples_per_symbol; t++) { +#if ENABLE_PSK31 + int16_t s = (cw_fill_msg_current.dit_duration != 0) ? + cw_generate_audio(omega, i, t) : + psk31_generate_audio(omega, i, t, samples_per_symbol); +#else + int16_t s = cw_generate_audio(omega, i, t); +#endif + + + if (buf_pos == AUDIO_BUF_LEN) { + xQueueSendToBack(cw_audio_queue, &cw_audio_buf, portMAX_DELAY); + buf_pos = 0; + } + cw_audio_buf[buf_pos++] = s; + + // Stereo + if (buf_pos == AUDIO_BUF_LEN) { + xQueueSendToBack(cw_audio_queue, &cw_audio_buf, portMAX_DELAY); + buf_pos = 0; + } + cw_audio_buf[buf_pos++] = s; + } + +#if ENABLE_PSK31 + if (cw_psk31_buffer[i] == 0) { + psk31_current_psk_phase *= -1; + } +#endif + } + + // We have completed this message + + cw_transmit_ongoing = 0; + + // Turn off audio to save power + audio_off(); + } + } +} + +int cw_psk31_busy(void) +{ + return cw_transmit_ongoing; +} + diff --git a/src/common/src/Core/common.c b/src/common/src/Core/common.c index c31bbf8..476d1b4 100644 --- a/src/common/src/Core/common.c +++ b/src/common/src/Core/common.c @@ -38,8 +38,7 @@ static uint16_t lfsr; static void common_increase_timestamp(TimerHandle_t t); -int find_last_sunday(const struct tm* time) -{ +int find_last_sunday(const struct tm* time) { struct tm t = *time; // the last sunday can never be before the 20th @@ -68,8 +67,7 @@ int find_last_sunday(const struct tm* time) * 1 if true * -1 in case of error */ -static int is_dst(const struct tm *time) -{ +static int is_dst(const struct tm *time) { /* DST from 01:00 UTC on last Sunday in March * to 01:00 UTC on last Sunday in October */ @@ -114,8 +112,7 @@ static int is_dst(const struct tm *time) } } -int local_time(struct tm *time) -{ +int local_time(struct tm *time) { const int local_time_offset=1; // hours int valid = gps_utctime(time); diff --git a/src/common/src/Core/main.c b/src/common/src/Core/main.c index 1a72e32..7760d6c 100644 --- a/src/common/src/Core/main.c +++ b/src/common/src/Core/main.c @@ -34,8 +34,8 @@ #include "semphr.h" /* Includes */ -/* #include "audio.h" */ -/* #include "cw.h" */ +#include "Audio/audio.h" +#include "Audio/cw.h" /* #include "pio.h" */ /* #include "i2c.h" */ #include "GPS/gps.h" @@ -119,11 +119,11 @@ static void test_task(void *pvParameters) { if (i == 0) { i = 1; - leds_turn_on(LED_RED); + leds_turn_on(LED_GREEN); } else { i = 0; - leds_turn_off(LED_RED); + leds_turn_off(LED_GREEN); } } @@ -134,8 +134,8 @@ static void test_task(void *pvParameters) { // already running when calling the init functions. static void launcher_task(void *pvParameters) { - /* usart_debug_puts("CW init\r\n"); */ - /* cw_psk31_init(16000); */ + usart_debug_puts("CW init\r\n"); + cw_psk31_init(16000); /* */ /* usart_debug_puts("PIO init\r\n"); */ /* pio_init(); */ @@ -143,17 +143,17 @@ static void launcher_task(void *pvParameters) /* usart_debug_puts("I2C init\r\n"); */ /* i2c_init(); */ /* */ - /* usart_debug_puts("common init\r\n"); */ - /* common_init(); */ - /* */ + usart_debug_puts("common init\r\n"); + common_init(); + usart_debug_puts("GPS init\r\n"); gps_init(); - /* */ + /* usart_debug_puts("DS18B20 init\r\n"); */ /* temperature_init(); */ /* */ /* usart_debug_puts("TaskButton init\r\n"); */ - /* */ + TaskHandle_t task_handle; /* xTaskCreate( */ /* detect_button_press, */ @@ -195,24 +195,24 @@ static void launcher_task(void *pvParameters) trigger_fault(FAULT_SOURCE_MAIN); } - /* usart_debug_puts("Audio init\r\n"); */ - /* */ - /* InitializeAudio(Audio16000HzSettings); */ - /* */ - /* usart_debug_puts("Audio set volume\r\n"); */ - /* SetAudioVolume(210); */ - /* */ - /* usart_debug_puts("Audio set callback\r\n"); */ - /* PlayAudioWithCallback(audio_callback, NULL); */ - /* */ - /* // By default, let's the audio off to save power */ - /* AudioOff(); */ + usart_debug_puts("Audio init\r\n"); + + audio_initialize(Audio16000HzSettings); + + usart_debug_puts("Audio set volume\r\n"); + audio_set_volume(210); + + usart_debug_puts("Audio set callback\r\n"); + audio_play_with_callback(audio_callback, NULL); + + // By default, let's the audio off to save power + audio_off(); usart_debug_puts("Init done.\r\n"); xTaskCreate( test_task, - "TaskFSM", + "Test task", 4*configMINIMAL_STACK_SIZE, (void*) NULL, tskIDLE_PRIORITY + 2UL, @@ -224,6 +224,10 @@ static void launcher_task(void *pvParameters) * for more info. */ + cw_psk31_push_message("HB9G HI - TESTING", 50, 440); + cw_psk31_push_message("HB9G HI AGAIN", 50, 440); + cw_psk31_push_message("HB9G 73", 50, 440); + while (1) { vTaskSuspend(NULL); } @@ -273,33 +277,33 @@ static void detect_button_press(void *pvParameters) /* } */ } -static void audio_callback(void* context, int select_buffer) -{ - /* static int16_t audio_buffer0[AUDIO_BUF_LEN]; */ - /* static int16_t audio_buffer1[AUDIO_BUF_LEN]; */ - /* int16_t *samples; */ - /* */ - /* if (select_buffer == 0) { */ - /* samples = audio_buffer0; */ - /* GPIO_ResetBits(GPIOD, GPIOD_BOARD_LED_RED); */ - /* select_buffer = 1; */ - /* } else { */ - /* samples = audio_buffer1; */ - /* GPIO_SetBits(GPIOD, GPIOD_BOARD_LED_RED); */ - /* select_buffer = 0; */ - /* } */ - /* */ - /* size_t samples_len = cw_psk31_fill_buffer(samples, AUDIO_BUF_LEN); */ - /* */ - /* if (samples_len == 0) { */ - /* for (int i = 0; i < AUDIO_BUF_LEN; i++) { */ - /* samples[i] = 0; */ - /* } */ - /* */ - /* samples_len = AUDIO_BUF_LEN; */ - /* } */ - /* */ - /* ProvideAudioBufferWithoutBlocking(samples, samples_len); */ +static void audio_callback(void* context, int select_buffer) { + static int16_t audio_buffer0[AUDIO_BUF_LEN]; + static int16_t audio_buffer1[AUDIO_BUF_LEN]; + int16_t *samples; + + if (select_buffer == 0) { + samples = audio_buffer0; + leds_turn_off(LED_RED); + select_buffer = 1; + } else { + samples = audio_buffer1; + leds_turn_on(LED_RED); + select_buffer = 0; + } + + size_t samples_len = cw_psk31_fill_buffer(samples, AUDIO_BUF_LEN); + + if (samples_len == 0) { + for (int i = 0; i < AUDIO_BUF_LEN; i++) { + samples[i] = 0; + } + + samples_len = AUDIO_BUF_LEN; + } + + audio_provide_buffer_without_blocking(samples, samples_len); + } static struct tm gps_time; |