From 3822a0a7e6145bd6d237e15ecbb5ac9d913024a5 Mon Sep 17 00:00:00 2001 From: nzasch <> Date: Mon, 24 Oct 2022 19:41:38 +0200 Subject: [PATCH] Initial import --- demodulator.c | 292 ++++++++++++++++++++++++++++++++++++++++++++++++++ demodulator.h | 80 ++++++++++++++ modem.c | 162 ++++++++++++++++++++++++++++ modem.h | 43 ++++++++ modem_types.h | 73 +++++++++++++ modulator.c | 70 ++++++++++++ modulator.h | 38 +++++++ ph_det_lp.s | 64 +++++++++++ pll.c | 20 ++++ preamble.c | 135 +++++++++++++++++++++++ preamble.h | 53 +++++++++ 11 files changed, 1030 insertions(+) create mode 100644 demodulator.c create mode 100644 demodulator.h create mode 100644 modem.c create mode 100644 modem.h create mode 100644 modem_types.h create mode 100644 modulator.c create mode 100644 modulator.h create mode 100644 ph_det_lp.s create mode 100644 pll.c create mode 100644 preamble.c create mode 100644 preamble.h diff --git a/demodulator.c b/demodulator.c new file mode 100644 index 0000000..ec8f07f --- /dev/null +++ b/demodulator.c @@ -0,0 +1,292 @@ +/* Title: demodulatore */ + +#include +#include + +#ifdef __XC16 +#include +#include +#endif + +#include "../phy/phy.h" // a chi cazzo serve? + +#include "../peripheral/adc.h" +#include "../peripheral/gpio.h" + +// serve? direi di si +#include "modem_types.h" + +#include "demodulator.h" + +// buffer dei simboli ricevuti +uint8_t rx_symbol_buffer[MAX_SYMBOL_BUFFER_SIZE]; +uint8_t rx_symbol_buffer_index, rx_symbol_buffer_lenght; + +uint8_t rx_sample_buffers_buffer[MAX_BUFFERS_PER_SYMBOL], rx_sample_buffer_index; + +uint8_t volatile process_input_sample_buffer; + +detector_symbol detector_constellation[MAX_MODULATION_ALPHABET_SIZE], symbol_error, min_symbol_distance; + +// zona del simbolo da esaminare: +uint8_t rx_symbol_detected_part_offset, rx_symbol_detected_part_length; + +// NCO +fractional tanlock_nco_rotor_inc; + +// ##################################################### + +void demodulator_setup(void) { + // RX + set_rx_GPIOs(); + + // setup rotore NCO demodulatore, posizionalo al centro della costellazione + tanlock_nco_rotor_inc = get_angular_frequency(find_constellation_center_values(mod.constellation).frequency); + + demodulator_prepare_constellation_symbols(mod.constellation, detector_constellation); + min_symbol_distance = demodulator_find_min_symbol_distance(detector_constellation); + + // zona del simbolo da esaminare: + // due quarti centrali del simbolo, da verificare + // si lavora solo su mezzo simbolo, ma si regge una sfasatura di +-1/4 di simbolo + rx_symbol_detected_part_offset = mod.buffers_per_symbol / 4; + rx_symbol_detected_part_length = mod.buffers_per_symbol / 2; +} + +// trova valori medii dei parametri di modulazione + +symbol find_constellation_center_values(symbol *constellation) { + uint8_t i; + symbol max, min, center; + max.amplitude = max.frequency = max.phase = 0; + min.frequency = min.phase = min.amplitude = 0xFFFF; + + for (i = 0; i < mod.alphabet_size; i++) { + if (constellation[i].frequency < min.frequency) min.frequency = constellation[i].frequency; + if (constellation[i].frequency > max.frequency) max.frequency = constellation[i].frequency; + + if (constellation[i].phase < min.phase) min.phase = constellation[i].phase; + if (constellation[i].phase > max.phase) max.phase = constellation[i].phase; + + if (constellation[i].amplitude < min.amplitude) min.amplitude = constellation[i].amplitude; + if (constellation[i].amplitude > max.amplitude) max.amplitude = constellation[i].amplitude; + + } + center.frequency = min.frequency + ((max.frequency - min.frequency) / 2); + center.phase = min.phase + ((max.phase - min.phase) / 2); + center.amplitude = min.amplitude + ((max.amplitude - min.amplitude) / 2); + return center; +} + +int16_t get_angular_frequency(uint16_t frequency) { + int32_t tmpfreq = (int32_t) frequency << 16; + return __builtin_divsd(tmpfreq, SAMPLING_RATE); +} + +void FIR_init(void) { +#ifdef __DSP_LIB__ + // inizializza filtri passa basso + FIRDelayInit(&ph_det_lpFilterQ); + FIRDelayInit(&ph_det_lpFilterI); + FIRDelayInit(&ph_det_lpFilterFreq); +#endif +} + +void demodulator_sample_buffer_demodulate(fractional *buffer, detector_symbol* outsymbol) { + uint8_t i; + fractional demodulator_frequency_diff = 0, demodulator_phase_diff = 0; + static fractional freq_lp[1]; + _Q15_16 frequency_accu = 0, phase_accu = 0, amplitude_accu = 0; + + for (i = 0; i < SAMPLES_PER_BUFFER; i++) { + // ampiezza + amplitude_accu += _Q15abs(buffer[i]); + + // fase + demodulator_phase_diff = demodulator_phase_detector(buffer[i]); + phase_accu += demodulator_phase_diff; + + // frequenza + demodulator_frequency_diff = demodulator_frequency_detector(demodulator_phase_diff); + +#ifdef __DSP_LIB__ + FIR(1, &freq_lp[0], &demodulator_frequency_diff, &ph_det_lpFilterFreq); +#endif + frequency_accu = frequency_accu + freq_lp[0]; + } + + // media + outsymbol->amplitude = __builtin_divsd(amplitude_accu, SAMPLES_PER_BUFFER); + outsymbol->phase = __builtin_divsd(phase_accu, SAMPLES_PER_BUFFER); + outsymbol->frequency = __builtin_divsd(frequency_accu, SAMPLES_PER_BUFFER); +} + +fractional demodulator_phase_detector(fractional sig_in) { + static fractional tanlock_nco_rotor_pos; + static fractional Q_mix_lp[1], I_mix_lp[1]; + // MIX + fractional I_mix, Q_mix, phasediff; + // moltiplica per segnale iniettato 171 cicli + + // perche' 15? perche i campioni sono da 12 bit + I_mix = _Q16shr((__builtin_mulss(sig_in, _Q15sinPI(tanlock_nco_rotor_pos))), 16); + Q_mix = _Q16shr((__builtin_mulss(sig_in, _Q15cosPI(tanlock_nco_rotor_pos))), 16); + +#ifdef __DSP_LIB__ + // filtra l'i/q 270 cicli + FIR(1, &I_mix_lp[0], &I_mix, &ph_det_lpFilterI); + FIR(1, &Q_mix_lp[0], &Q_mix, &ph_det_lpFilterQ); +#endif + + // incrementa il rotore dell'oscillatore locale + tanlock_nco_rotor_pos += tanlock_nco_rotor_inc; // non deve saturare! + + // calcola arcotangente, circa 420 cicli + phasediff = _Q15atan2circle(I_mix_lp[0], Q_mix_lp[0]); + // phasediff = _Q15atan2circle(I_mix, Q_mix); + + return phasediff; +} + +fractional demodulator_frequency_detector(fractional phase) { + static fractional demodulator_last_phase, demodulator_last_delta; + fractional demodulator_phase_delta = 0, demodulator_phase_delta_tmp = 0; + + demodulator_phase_delta_tmp = _Q15sub(phase, demodulator_last_phase); + + // antiskew + if (_Q15abs(demodulator_phase_delta_tmp) < MAX_PHASE_SKEW) demodulator_phase_delta = demodulator_phase_delta_tmp; + else { + demodulator_phase_delta = demodulator_last_delta; + } + + // storici + demodulator_last_phase = phase; + demodulator_last_delta = demodulator_phase_delta; + + return demodulator_phase_delta; +} + +// ####### symbolame ########## + +void demodulator_prepare_constellation_symbols(symbol *constellation, detector_symbol *demod_constellation) { + uint8_t i; + for (i = 0; i < mod.alphabet_size; i++) { + demod_constellation[i].frequency = _Q15sub(get_angular_frequency(constellation[i].frequency), tanlock_nco_rotor_inc); + demod_constellation[i].phase = constellation[i].phase; + demod_constellation[i].amplitude = constellation[i].amplitude; + } +} + +detector_symbol demodulator_find_min_symbol_distance(detector_symbol *demod_constellation) { + // find minimum distance between symbols + uint8_t i, k; + detector_symbol tmp_distance, min_distance; + min_distance.frequency = min_distance.phase = min_distance.amplitude = 0x7FFF; + for (i = 0; i < mod.alphabet_size; i++) { + for (k = 0; k < mod.alphabet_size; k++) { + if (k != i) { + tmp_distance.frequency = _Q15abs(_Q15sub(demod_constellation[i].frequency, demod_constellation[k].frequency)); + if (tmp_distance.frequency < min_distance.frequency) min_distance.frequency = tmp_distance.frequency; + + tmp_distance.phase = _Q15sub(demod_constellation[i].phase, demod_constellation[k].phase); + tmp_distance.amplitude = _Q15sub(demod_constellation[i].amplitude, demod_constellation[k].amplitude); + } + } + } + return min_distance; +} + +// trova il simbolo piu vicino nella costellazione + +uint8_t demodulator_find_nearest_symbol(detector_symbol *in_symbol, detector_symbol *target_constellation, detector_symbol *moderror) { + detector_symbol distance, min_distance = {0x7FFF, 0x7FFF, 0x7FFF}; + uint8_t i, nearest_symbol = 0; + for (i = 0; i < mod.alphabet_size; i++) { + if (mod.frequency_keying) { + distance.frequency = _Q15abs(_Q15sub(target_constellation[i].frequency, in_symbol->frequency)); + if (distance.frequency < min_distance.frequency) { + min_distance.frequency = distance.frequency; + nearest_symbol = i; + moderror->frequency = distance.frequency; + } + } + } + + return nearest_symbol; +} + +// trova deviazione dal simbolo + +uint8_t find_symbol_deviation(uint8_t *serie, uint8_t mean, uint8_t len) { + int16_t sum_deviation = 0; + uint8_t i; + for (i = 0; i < len; ++i) + sum_deviation += (serie[i] - mean)*(serie[i] - mean); + return sqrt(sum_deviation / len); +} +// valore ricorrente + +uint8_t demodulator_most_frequent_value(uint8_t *serie, uint8_t range, uint8_t len) { + uint8_t i, prezzemolo = 0, prezzemolo_occurrences = 0; + uint8_t occurrencez[MAX_MODULATION_ALPHABET_SIZE] = {0}; + // conta ricorrenze per ogni simbolo + for (i = 0; i < len; i++) { + occurrencez[serie[i]]++; + } + + // trova il simbolo con la ricorrenza maggiore + for (i = 0; i < range; i++) { + if (occurrencez[i] > prezzemolo_occurrences) { + prezzemolo = i; + prezzemolo_occurrences = occurrencez[i]; + } + } + return prezzemolo; +} + +fractional _Q15atan2circle(fractional Y, fractional X) { + // ARCTAN a cerchio pieno + fractional atang = 0, offset = 0, temp = 0; + fractional X_sign = X; + + if (Y <= 0) { + temp = Y; + Y = X; + X = _Q15neg(temp); + offset = _Q15add(offset, 16384); + } + + if (_Q15abs(Y) <= _Q15abs(X)) { + if (X_sign < 0) { + temp = _Q15add(X, Y); + Y = _Q15sub(Y, X); + } else { + temp = _Q15sub(X, Y); + Y = _Q15add(Y, X); + } + + X = temp; + offset = _Q15add(offset, 8192); + } + // circa 300 cy +#ifdef _libq_h_ + atang = _Q15atanYByXByPI(Y, X); // vuole il large code model ? no! 2.2.2 del man +#endif + if (atang < 0) { + atang = _Q15sub(atang, offset); + } else { + atang = _Q15add(atang, offset); + } + return atang; +} + +// devel +/* +fractional delay_phase_detector(fractional sig_in) { + fractional phased_sig_in, phasediff; + phased_sig_in = delay(sig_in, 2, delay_buffer); + phasediff = _Q15atan2circle(phased_sig_in, sig_in); + return phasediff; +} + */ \ No newline at end of file diff --git a/demodulator.h b/demodulator.h new file mode 100644 index 0000000..631ddae --- /dev/null +++ b/demodulator.h @@ -0,0 +1,80 @@ +/* + * File: demodulator.h + */ + +#ifndef DEMODULATOR_H +#define DEMODULATOR_H + +#ifdef __XC16 +#include +#else +typedef int16_t _Q0_15; +typedef int32_t _Q15_16; + +typedef _Q0_15 _Q15; +typedef _Q15_16 _Q16; + +typedef int16_t fractional; +#endif + +#include +#include "modem_types.h" + +#define MAX_PHASE_SKEW 10000L +#define ADC_MAX_AMPLITUDE 10000L + +typedef struct { + fractional frequency; + fractional phase; + fractional amplitude; +} detector_symbol; + +extern detector_symbol detector_constellation[MAX_MODULATION_ALPHABET_SIZE], symbol_error, min_symbol_distance; + +// TODO + +typedef struct { + uint8_t buffer[MAX_SYMBOL_BUFFER_SIZE]; + uint8_t index; + uint8_t length; +} symbol_buffer; + +// extern symbol_buffer rx_symbol_buffer; + +// buffer dei simboli +extern uint8_t rx_symbol_buffer[MAX_SYMBOL_BUFFER_SIZE]; +extern uint8_t rx_symbol_buffer_index, rx_symbol_buffer_lenght; + +extern uint8_t rx_sample_buffers_buffer[MAX_BUFFERS_PER_SYMBOL], rx_sample_buffer_index; + +extern uint8_t volatile process_input_sample_buffer; + +// zona del simbolo da esaminare: +extern uint8_t rx_symbol_detected_part_offset, rx_symbol_detected_part_length; + +// NCO +extern fractional tanlock_nco_rotor_inc; + +#ifdef __DSP_LIB__ +extern FIRStruct ph_det_lpFilterI, ph_det_lpFilterQ, ph_det_lpFilterFreq; +#endif + +void demodulator_setup(void); +void demodulator_sample_buffer_demodulate(fractional *buffer, detector_symbol* outsymbol); + +symbol find_constellation_center_values(symbol *constellation); +int16_t get_angular_frequency(uint16_t frequency); +void demodulator_prepare_constellation_symbols(symbol *constellation, detector_symbol *demod_constellation); +detector_symbol demodulator_find_min_symbol_distance(detector_symbol *demod_constellation); +uint8_t demodulator_most_frequent_value(uint8_t *serie, uint8_t range, uint8_t len); +uint8_t demodulator_find_nearest_symbol(detector_symbol *in_symbol, detector_symbol *target_constellation, detector_symbol *moderror); + +fractional demodulator_phase_detector(fractional sig_in); +fractional demodulator_frequency_detector(fractional phase); + +fractional _Q15atan2circle(fractional Y, fractional X); +void FIR_init(void); + + +#endif /* DEMODULATOR_H */ + diff --git a/modem.c b/modem.c new file mode 100644 index 0000000..60de78b --- /dev/null +++ b/modem.c @@ -0,0 +1,162 @@ +/* armando modem - iz4kll */ + +#ifdef __XC16 +#include +#include +#endif + +#include + +#include "modem.h" + +#include "../peripheral/gpio.h" +#include "../peripheral/adc.h" +#include "../peripheral/dac.h" + +#if defined(__DEBUG) && ! defined(__MPLAB_DEBUGGER_PK3) +extern fractional debug_value1, debug_value2, debug_value3; +#endif + +// parametri della modulazione +modulation mod; + +uint8_t modem_tx_state, modem_rx_state; + + +// predelay +uint16_t modem_predelay_mstime, modem_predelay_buffer_size, modem_predelay_buffer_index; + +// non trasmettere quando stai ricevendo payload phy +uint8_t modem_avoid_overlap; + +// payload +uint16_t modem_payload_max_symbols, modem_payload_symbols_index; + +// solo al boot + +void modem_init(void) { + FIR_init(); + /// Initialize the A/D converter + adc_init(); + adc_init_dma(); + /// Initialize the Timer to generate sampling event for ADC + adc_init_tmr(); + + modem_preamble_waiting = 0; +#ifndef __DEBUG + // PENDING + InitDACDMA(); // prima di initdac!! + initDac(); // Initialize the D/A converter +#endif + +} + +// ad ogni cambio modulazione + +void modem_set_up(void) { + /// imposta dimensione alfabeto + mod.alphabet_size = (1 << mod.bits_per_symbol); + /// calcola numero di buffer per simbolo + mod.buffers_per_symbol = BUFFER_RATE / mod.symbol_rate; + + /// setup modulatore + modulator_setup(); + + /// setup demodulatore + demodulator_setup(); + + modem_set_up_predelay(); + + // va a valle del popolamento della costellazione di detect + modem_set_up_preamble(); +} + +// loopback per test + +void DAC_ADC_loopback(void){ +#ifdef __DSP_LIB__ + /// Travaso DAC-> ADC per test + VectorCopy(SAMPLES_PER_BUFFER, ADCBuffer[dac_buffer_toggle], Q_DACBuffer[dac_buffer_toggle]); +#endif +} + +void modem_process_sample_buffer(fractional *buffer) { + static detector_symbol detector_modulation_averages; + + // demodula un buffer di campioni e torna le medie di freq, ph e amp + demodulator_sample_buffer_demodulate(buffer, &detector_modulation_averages); + + // spia OverLoad +#ifdef USE_GPIO + if ((gpio_mode == GPIO_INFO) && (detector_modulation_averages.amplitude > ADC_MAX_AMPLITUDE)) GPIO_ADC_OL = 1; + else { + GPIO_ADC_OL = 0; + } +#endif + +#if ! defined(__DEBUG) + // viene riasserito dall'interrupt DMA2, meglio azzerare presto per non perdere eventi + process_input_sample_buffer = 0; // TEST quando in debug ci pensa chi ? +#endif + +#if defined(__DEBUG) && ! defined(__MPLAB_DEBUGGER_PK3) + // stampa frequenza vista dal demodulatore + debug_value2 = detector_modulation_averages.frequency; +#endif + // aspetta un preambolo + if (modem_preamble_waiting) { + if (modem_detect_preamble(detector_modulation_averages.frequency)) { + // preambolo trovato + modem_preamble_waiting = 0; + modem_post_preamble_timer = modem_post_preamble_delay; +#ifdef USE_GPIO + if (gpio_mode == GPIO_INFO) GPIO_PHY_PAYLOAD = 1; +#endif + } + } else if (modem_post_preamble_timer > 0) { + modem_post_preamble_timer--; // aspetta + } else { + // comincia a ricevere i dati + // aspetta header o dati + if (rx_sample_buffer_index < mod.buffers_per_symbol) { + // confronta le medie con la costellazione e torna il simbolo piu vicino + rx_sample_buffers_buffer[rx_sample_buffer_index] = demodulator_find_nearest_symbol(&detector_modulation_averages, detector_constellation, &symbol_error); + +#ifdef USE_GPIO + // qualita' TODO + GPIO_SYM_OK = 0; + if (symbol_error.frequency < (min_symbol_distance.frequency / 4)) GPIO_SYM_OK = 1; + else { + GPIO_SYM_OK = 0; + } +#endif + // sarebbe preferibile avere uno storico dei valori, invece che dei simboli, ma sarebbe MAX_BUFFERS_PER_SYMBOL * detector_symbol + // anche accumulare una media sembra critico + } + rx_sample_buffer_index++; + +#if defined(__DEBUG) && ! defined(__MPLAB_DEBUGGER_PK3) + // value3 = rx_sample_buffer_index; + debug_value3 = symbol_error.frequency; +#endif + + // numero di buffer per simbolo raggiunto + if (rx_sample_buffer_index >= mod.buffers_per_symbol) { + // valore piu ricorrente + rx_symbol_buffer[rx_symbol_buffer_index] = demodulator_most_frequent_value(&rx_sample_buffers_buffer[rx_symbol_detected_part_offset], mod.alphabet_size, rx_symbol_detected_part_length); + + // deviazione TODO + // find_symbol_deviation(&rx_sample_buffers_buffer[rx_symbol_detected_part_offset], rx_symbol_buffer[rx_symbol_buffer_index], rx_symbol_detected_part_length); + + // simbolo fatto, avanza al successivo + rx_symbol_buffer_index++; + rx_sample_buffer_index = 0; + // stava qua + } + } +} + +void modem_set_up_predelay(void) { + silence = (rotor_symbol){0, 0, 0}; + modem_predelay_buffer_size = (modem_predelay_mstime * BUFFER_RATE) / 1000; +} \ No newline at end of file diff --git a/modem.h b/modem.h new file mode 100644 index 0000000..45eef40 --- /dev/null +++ b/modem.h @@ -0,0 +1,43 @@ +/** @file + * @brief armando, strato modem + * + * Lo strato modem e' diviso nella sezione modulator (tx) e demodulator (rx) e scambia dati: + * - in basso con adc/dac/gpio + * in rx con ADCBuffer + * in tx con DACBuffer + * - in alto con lo strato phy: + * in rx con rx_symbol_buffer e rx_symbol_buffer_index + * in tx con tx_symbol_buffer e tx_symbol_buffer_index + * + * La configurazione dello strato e' definita dalla struttura modulation + */ + +#ifndef MODEM_H +#define MODEM_H + +#include + +#ifdef __XC16 +#include +#endif + +#include "modem_types.h" + +#include "modulator.h" +#include "demodulator.h" +#include "preamble.h" + +// protoz +void modem_set_up(void); +void modem_init(void); + +void DAC_ADC_loopback(void); + +// todo +int pll_frequency_servo(fractional frequency_error); + +void modem_process_sample_buffer(fractional *buffer); + +void modem_set_up_predelay(void); + +#endif \ No newline at end of file diff --git a/modem_types.h b/modem_types.h new file mode 100644 index 0000000..825a151 --- /dev/null +++ b/modem_types.h @@ -0,0 +1,73 @@ +/* + * File: modem_types.h + */ + +/** @file */ + +#ifndef MODEM_TYPES_H +#define MODEM_TYPES_H + +// Sampling Control +// il buffer rate deve essere > fmod_min/2 +// il buffer rate deve > symbol rate + +#define SAMPLING_RATE 16000UL ///< frequenza di campionamento +#define SAMPLES_PER_BUFFER 32U ///< dimensione del buffer dei campioni +#define BUFFER_RATE (SAMPLING_RATE / SAMPLES_PER_BUFFER) ///< frequenza di buffer: deve essere > di MAX_SYMBOL_RATE * 3 + +#define MAX_SYMBOL_RATE 500U +#define MIN_SYMBOL_RATE 20U //umm, se troppo piccolo da problemi nelle dimensioni dei buffer + +#define MIN_BUFFERS_PER_SYMBOL (BUFFER_RATE / MAX_SYMBOL_RATE) +#define MAX_BUFFERS_PER_SYMBOL (BUFFER_RATE / MIN_SYMBOL_RATE) + +#define MAX_MODULATION_ALPHABET_SIZE 16U ///< massimo numero di simboli nella costellazione + +#define MAX_SYMBOL_BUFFER_SIZE 512U ///< MAX_FEC_OUTPUT_SIZE * MAX_FEC_PER_BLOCK + +#define MODEM_STATE_PREDELAY_WAIT 0 +#define MODEM_STATE_PREDELAY_PROCESS 1 +#define MODEM_STATE_PREDELAY_DONE 2 +#define MODEM_STATE_PREAMBLE_WAIT 3 +#define MODEM_STATE_PREAMBLE_PROCESS 4 +#define MODEM_STATE_PREAMBLE_DONE 5 +#define MODEM_STATE_POST_PREAMBLE_WAIT 6 +#define MODEM_STATE_POST_PREAMBLE_PROCESS 7 +#define MODEM_STATE_POST_PREAMBLE_DONE 8 +#define MODEM_STATE_PAYLOAD_WAIT 9 +#define MODEM_STATE_PAYLOAD_PROCESS 10 +#define MODEM_STATE_PAYLOAD_DONE 11 +#define MODEM_STATE_IDLE 12 + +typedef struct { + uint16_t frequency; ///< frequenza del simbolo + uint16_t phase; ///< fase del simbolo + uint16_t amplitude; ///< ampiezza del simbolo +} symbol; + +// parametri della modulazione + +typedef struct { + uint16_t symbol_rate; ///< numero di simboli per secondo + uint8_t buffers_per_symbol; ///< numero di buffer di campioni in un simbolo + uint8_t bits_per_symbol; ///< bit di informazione espressi da un simbolo + uint8_t alphabet_size; ///< numero di possibili simboli che compongono la modulazione + uint8_t amplitude_keying; ///< manipolazione di ampiezza + uint8_t frequency_keying; ///< manipolazione di frequenza + uint8_t phase_keying; ///< manipolazione di fase + symbol constellation[MAX_MODULATION_ALPHABET_SIZE]; ///< descrizione della costellazione +} modulation; + +extern modulation mod; + +extern uint8_t modem_tx_state, modem_rx_state; + +extern uint16_t modem_predelay_mstime, modem_predelay_buffer_size, modem_predelay_buffer_index; + +extern uint8_t modem_avoid_overlap; + +// payload +extern uint16_t modem_payload_max_symbols, modem_payload_symbols_index; + +#endif /* MODEM_TYPES_H */ + diff --git a/modulator.c b/modulator.c new file mode 100644 index 0000000..9e31ec7 --- /dev/null +++ b/modulator.c @@ -0,0 +1,70 @@ +/* armando modem - 2014 */ + +#ifdef __XC16 +#include +#include +#include +#elif __unix + +#endif + +#include + +#include "modem.h" +#include "modulator.h" + +#include "../peripheral/dac.h" +#include "../peripheral/gpio.h" + +rotor_symbol rotor_constellation[MAX_MODULATION_ALPHABET_SIZE]; +rotor_symbol silence; + +// buffer dei simboli +uint8_t tx_symbol_buffer[MAX_SYMBOL_BUFFER_SIZE]; +uint8_t tx_symbol_buffer_index, tx_symbol_buffer_lenght; + +void modulator_setup(void) { + // TX +#ifdef USE_GPIO + set_tx_GPIOs(); +#endif + modulator_prepare_constellation_symbols(mod.constellation, rotor_constellation); +} + +// ##################################################### + +// genera un buffer di campioni + +void modulator_generate_sample_buffer(fractional *Q_buffer, fractional *I_buffer, uint8_t size, rotor_symbol symbol) { + uint8_t i; + + static fractional modulator_rotor, modulator_rotor_position; + i = 0; + while (i < size) { + modulator_rotor_position = modulator_rotor + symbol.offset; + Q_buffer[i] = __builtin_mulss(_Q15sinPI(modulator_rotor_position), // non deve saturare + symbol.gain) >> 15; + + I_buffer[i] = __builtin_mulss(_Q15cosPI(modulator_rotor_position), // non deve saturare + symbol.gain) >> 15; + + modulator_rotor += symbol.increment; // la _Q15 satura! + ++i; + } + +#if ! defined(__DEBUG) + // viene riasserito dal DMA del dac, dove mettere? + populate_tx_sample_buffer = 0; // fatto +#endif +} + +// traduce da modulazione astratta a parametri per il modulatore + +void modulator_prepare_constellation_symbols(symbol *constellation, rotor_symbol *mod_constellation) { + uint8_t i; + for (i = 0; i < mod.alphabet_size; i++) { + mod_constellation[i].increment = get_angular_frequency(constellation[i].frequency); + mod_constellation[i].offset = constellation[i].phase; + mod_constellation[i].gain = constellation[i].amplitude; + } +} \ No newline at end of file diff --git a/modulator.h b/modulator.h new file mode 100644 index 0000000..0342a04 --- /dev/null +++ b/modulator.h @@ -0,0 +1,38 @@ +/* + * File: modulator.h + */ + +#ifndef MODULATOR_H +#define MODULATOR_H + +#ifdef __XC16 +#include +#else +typedef int16_t fractional; +#define _Q15 _Q15sinPI(_Q15); +#endif + +#include +#include "modem.h" + +typedef struct { + fractional increment; + fractional offset; + fractional gain; +} rotor_symbol; + +extern rotor_symbol rotor_constellation[MAX_MODULATION_ALPHABET_SIZE]; +extern rotor_symbol silence; + +// buffer dei simboli +extern uint8_t tx_symbol_buffer[MAX_SYMBOL_BUFFER_SIZE]; +extern uint8_t tx_symbol_buffer_index, tx_symbol_buffer_lenght; + +// protoz +void modulator_setup(void); +void modulator_generate_sample_buffer(fractional *Q_buffer, fractional *I_buffer, uint8_t size, rotor_symbol symbol); +void modulator_prepare_constellation_symbols(symbol *constellation, rotor_symbol *mod_constellation); +void set_GPIO_bit(uint8_t symbol); + +#endif /* MODULATOR_H */ + diff --git a/ph_det_lp.s b/ph_det_lp.s new file mode 100644 index 0000000..9737c75 --- /dev/null +++ b/ph_det_lp.s @@ -0,0 +1,64 @@ +.equ ph_det_lpNumTaps, 63 + + +.section .xdata +.align 128 + +ph_det_lpTaps: +.hword 0xFFB5, 0xFFD2, 0xFFF3, 0x0014, 0x0039, 0x0060, 0x0089, 0x00B3, 0x00DF +.hword 0x010C, 0x013A, 0x0169, 0x0198, 0x01C7, 0x01F6, 0x0224, 0x0251, 0x027D +.hword 0x02A7, 0x02D0, 0x02F7, 0x031B, 0x033C, 0x035B, 0x0377, 0x038F, 0x03A4 +.hword 0x03B6, 0x03C3, 0x03CD, 0x03D3, 0x03D5, 0x03D3, 0x03CD, 0x03C3, 0x03B6 +.hword 0x03A4, 0x038F, 0x0377, 0x035B, 0x033C, 0x031B, 0x02F7, 0x02D0, 0x02A7 +.hword 0x027D, 0x0251, 0x0224, 0x01F6, 0x01C7, 0x0198, 0x0169, 0x013A, 0x010C +.hword 0x00DF, 0x00B3, 0x0089, 0x0060, 0x0039, 0x0014, 0xFFF3, 0xFFD2, 0xFFB5 + +.section .ybss, "b" +.align 128 + +ph_det_lpDelayI: +.space ph_det_lpNumTaps*2 + +ph_det_lpDelayQ: +.space ph_det_lpNumTaps*2 + +ph_det_lpDelayFreq: +.space ph_det_lpNumTaps*2 + +.section .data +.global _ph_det_lpFilterI + +_ph_det_lpFilterI: +.hword ph_det_lpNumTaps +.hword ph_det_lpTaps +.hword ph_det_lpTaps+ph_det_lpNumTaps*2-1 +.hword 0xff00 +.hword ph_det_lpDelayI +.hword ph_det_lpDelayI+ph_det_lpNumTaps*2-1 +.hword ph_det_lpDelayI + + +.section .data +.global _ph_det_lpFilterQ + +_ph_det_lpFilterQ: +.hword ph_det_lpNumTaps +.hword ph_det_lpTaps +.hword ph_det_lpTaps+ph_det_lpNumTaps*2-1 +.hword 0xff00 +.hword ph_det_lpDelayQ +.hword ph_det_lpDelayQ+ph_det_lpNumTaps*2-1 +.hword ph_det_lpDelayQ + +.section .data +.global _ph_det_lpFilterFreq + +_ph_det_lpFilterFreq: +.hword ph_det_lpNumTaps +.hword ph_det_lpTaps +.hword ph_det_lpTaps+ph_det_lpNumTaps*2-1 +.hword 0xff00 +.hword ph_det_lpDelayFreq +.hword ph_det_lpDelayFreq+ph_det_lpNumTaps*2-1 +.hword ph_det_lpDelayFreq + \ No newline at end of file diff --git a/pll.c b/pll.c new file mode 100644 index 0000000..ac8a567 --- /dev/null +++ b/pll.c @@ -0,0 +1,20 @@ +#include + +#ifdef __XC16 +#include +#include +#endif + +#include "demodulator.h" + +int pll_frequency_servo(fractional frequency_error) { + fractional frequency_servo_feedback; + + if (_Q15abs(frequency_error) < 1) frequency_servo_feedback = frequency_error; + else { + frequency_servo_feedback = _Q15shr(frequency_error, 6); + } + + return frequency_servo_feedback; +} + diff --git a/preamble.c b/preamble.c new file mode 100644 index 0000000..9b3b06e --- /dev/null +++ b/preamble.c @@ -0,0 +1,135 @@ +#include +#include + +// debug +#ifdef __XC16 +#include +#endif + +#ifdef __XC16 +#include +#include +#endif + +#include "../ringbuf.h" +#include "modem_types.h" + +#include "preamble.h" + +#include "../peripheral/gpio.h" //debug +#include "../mini-printf.h" + + +#if defined(__DEBUG) && ! defined(__MPLAB_DEBUGGER_PK3) +#include "../peripheral/uarts.h" +extern fractional debug_value3; +#endif + +// preambolo +uint8_t modem_preamble_waiting, modem_post_preamble_delay, modem_post_preamble_timer; +// uint8_t modem_preamble_pending, modem_preamble_done; +uint16_t BuffersPerPreamble; + +fractional sync_values_sequence[(MODEM_PREAMBLE_LENGTH * MAX_BUFFERS_PER_SYMBOL)]; +_Q15_16 preamble_squelch_distance; + +void modem_set_up_preamble(void) { + uint8_t sync_symbols[MODEM_PREAMBLE_LENGTH]; + modem_generate_preamble(sync_symbols); + modem_generate_preamble_fingerprint(sync_symbols, sync_values_sequence, mod.buffers_per_symbol); + BuffersPerPreamble = MODEM_PREAMBLE_LENGTH * mod.buffers_per_symbol; + + preamble_squelch_distance = _Q16mpy(modem_get_preamble_max_correlation_index(sync_symbols), _Q16ftoi(MODEM_PREAMBLE_SQUELCH)); + + modem_post_preamble_delay = BUFFER_RATE / mod.symbol_rate; // un simbolo +} + +uint8_t modem_generate_preamble(uint8_t *ModulationSymbolsBuffer) { + uint8_t i; + // barker code con N=13 + uint8_t sync_sequence[13] = {1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1}; + + for (i = 0; i < MODEM_PREAMBLE_LENGTH; i++) { + if (sync_sequence[i]) { + ModulationSymbolsBuffer[i] = (mod.alphabet_size - 1); + } else { + ModulationSymbolsBuffer[i] = 0; + } + } + return MODEM_PREAMBLE_LENGTH; +} + +// trasforma la sequenza di simboli nella sua rappresentazione in sottosimboli + +void modem_generate_preamble_fingerprint(uint8_t *ModulationSymbolsBuffer, fractional *symbols_values_sequence, uint8_t values_per_symbol) { + uint8_t i, l, m = 0; + for (i = 0; i < MODEM_PREAMBLE_LENGTH; i++) { + for (l = 0; l < values_per_symbol; l++) { + symbols_values_sequence[m] = detector_constellation[ModulationSymbolsBuffer[i]].frequency; + m++; + } + } +} + +// trova indice di correlazione massimo per la data modulationz + +_Q15_16 modem_get_preamble_max_correlation_index(uint8_t *ModulationSymbolsBuffer) { + uint8_t i; + _Q15_16 symbol_mass = 0; + _Q15_16 mass = 0; + for (i = 0; i < MODEM_PREAMBLE_LENGTH; i++) { + symbol_mass = __builtin_mulss( + detector_constellation[ModulationSymbolsBuffer[i]].frequency, + detector_constellation[ModulationSymbolsBuffer[i]].frequency); + mass += symbol_mass; + } + return mass * mod.buffers_per_symbol; +} +/// riconosce preambolo + +uint8_t modem_detect_preamble(fractional insymbol) { + static _Q15_16 somigl_hist; + static fractional in_sym_ringbuf[MODEM_PREAMBLE_SEARCH_BUFF_SIZE]; + static uint16_t in_sym_ringbuf_index; + + uint8_t found = 0; + uint16_t i; + _Q15_16 distance_accu = 0; + + // mette nel ringbuffer + in_sym_ringbuf[in_sym_ringbuf_index] = insymbol; + + // correla + for (i = 0; i < BuffersPerPreamble; i++) { + // product + + distance_accu += __builtin_mulss( + in_sym_ringbuf[ringbuf_idx_from_end(&in_sym_ringbuf_index, MODEM_PREAMBLE_SEARCH_BUFF_SIZE_MASK, (BuffersPerPreamble - i))], + sync_values_sequence[i]); + } + + +#if defined(__DEBUG) && ! defined(__MPLAB_DEBUGGER_PK3) + debug_value3 = _Q16shr(distance_accu, 16); +#endif +/* + if (distance_accu > preamble_squelch_distance) LATBbits.LATB3 = 1; + else { + LATBbits.LATB3 = 0; + } + */ + if ((distance_accu > preamble_squelch_distance) && (somigl_hist > distance_accu)) { + // trovato ! + + //resetta il riconoscimento di preambolo + somigl_hist = 0; + memset(in_sym_ringbuf, 0, MODEM_PREAMBLE_SEARCH_BUFF_SIZE * sizeof (in_sym_ringbuf)); + in_sym_ringbuf_index = 0; + + found = 1; + } else { + somigl_hist = distance_accu; + ringbuf_increment(&in_sym_ringbuf_index, MODEM_PREAMBLE_SEARCH_BUFF_SIZE_MASK); + } + return found; +} \ No newline at end of file diff --git a/preamble.h b/preamble.h new file mode 100644 index 0000000..56451f3 --- /dev/null +++ b/preamble.h @@ -0,0 +1,53 @@ +/* + * File: preamble.h + * Author: nzasch + * + * Created on 12 gennaio 2017, 15.05 + */ + +#ifndef PREAMBLE_H +#define PREAMBLE_H + +#include + +#ifdef __XC16 +#include +#include +#else +typedef int16_t _Q0_15; +typedef int32_t _Q15_16; + +typedef _Q0_15 _Q15; +typedef _Q15_16 _Q16; + +typedef int16_t fractional; +#endif + +#include "modem_types.h" +#include "demodulator.h" + +#define MODEM_PREAMBLE_LENGTH 13U +#define MODEM_PREAMBLE_SQUELCH 0.7f // da 0 a 1 + +// ((16000 ? 32) ? 20) ? 13 = 325 +#define MODEM_PREAMBLE_SEARCH_BUFF_SIZE 512U // circa (MODEM_PREAMBLE_LENGTH * MAX_BUFFERS_PER_SYMBOL) ma deve esere ^ di 2 +#define MODEM_PREAMBLE_SEARCH_BUFF_SIZE_MASK (MODEM_PREAMBLE_SEARCH_BUFF_SIZE - 1) + +extern fractional sync_values_sequence[MODEM_PREAMBLE_LENGTH * MAX_BUFFERS_PER_SYMBOL]; ///< buffer che contiene il modello di sequenza +extern _Q15_16 preamble_squelch_distance; ///< squelch pesato sulla lunghezza del preambolo + +// extern uint8_t modem_preamble_pending, modem_preamble_done; +extern uint8_t modem_preamble_waiting, modem_post_preamble_delay, modem_post_preamble_timer; +extern uint16_t BuffersPerPreamble; + +// protoz +void modem_set_up_preamble(void); + +uint8_t modem_generate_preamble(uint8_t *ModulationSymbolsBuffer); +void modem_generate_preamble_fingerprint(uint8_t *ModulationSymbolsBuffer, fractional *symbols_values_sequence, uint8_t values_per_symbol); + +_Q15_16 modem_get_preamble_max_correlation_index(uint8_t *ModulationSymbolsBuffer); +uint8_t modem_detect_preamble(fractional insymbol); + +#endif /* PREAMBLE_H */ +