Initial import

This commit is contained in:
nzasch
2022-10-24 19:41:38 +02:00
commit 3822a0a7e6
11 changed files with 1030 additions and 0 deletions

292
demodulator.c Normal file
View File

@@ -0,0 +1,292 @@
/* Title: demodulatore */
#include <stdint.h>
#include <math.h>
#ifdef __XC16
#include <dsp.h>
#include <libq.h>
#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;
}
*/

80
demodulator.h Normal file
View File

@@ -0,0 +1,80 @@
/*
* File: demodulator.h
*/
#ifndef DEMODULATOR_H
#define DEMODULATOR_H
#ifdef __XC16
#include <dsp.h>
#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 <stdint.h>
#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 */

162
modem.c Normal file
View File

@@ -0,0 +1,162 @@
/* armando modem - iz4kll */
#ifdef __XC16
#include <libq.h>
#include <dsp.h>
#endif
#include <stdint.h>
#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;
}

43
modem.h Normal file
View File

@@ -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 <stdint.h>
#ifdef __XC16
#include <dsp.h>
#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

73
modem_types.h Normal file
View File

@@ -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 */

70
modulator.c Normal file
View File

@@ -0,0 +1,70 @@
/* armando modem - 2014 */
#ifdef __XC16
#include <xc.h>
#include <dsp.h>
#include <libq.h>
#elif __unix
#endif
#include <stdint.h>
#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;
}
}

38
modulator.h Normal file
View File

@@ -0,0 +1,38 @@
/*
* File: modulator.h
*/
#ifndef MODULATOR_H
#define MODULATOR_H
#ifdef __XC16
#include <dsp.h>
#else
typedef int16_t fractional;
#define _Q15 _Q15sinPI(_Q15);
#endif
#include <stdint.h>
#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 */

64
ph_det_lp.s Normal file
View File

@@ -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

20
pll.c Normal file
View File

@@ -0,0 +1,20 @@
#include <stdint.h>
#ifdef __XC16
#include <libq.h>
#include <dsp.h>
#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;
}

135
preamble.c Normal file
View File

@@ -0,0 +1,135 @@
#include <stdint.h>
#include <string.h>
// debug
#ifdef __XC16
#include <xc.h>
#endif
#ifdef __XC16
#include <libq.h>
#include <dsp.h>
#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;
}

53
preamble.h Normal file
View File

@@ -0,0 +1,53 @@
/*
* File: preamble.h
* Author: nzasch
*
* Created on 12 gennaio 2017, 15.05
*/
#ifndef PREAMBLE_H
#define PREAMBLE_H
#include <stdint.h>
#ifdef __XC16
#include <libq.h>
#include <dsp.h>
#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 */