[New feature] Generally working olivia / contestia implementation. Only decodes sometimes... Maybe needs a preamble?

geofence_dev
Richard Eoin Meadows 2014-11-26 11:56:21 +00:00
rodzic 5f9735805e
commit 69e1ab99ca
9 zmienionych plików z 262 dodań i 139 usunięć

Plik binarny nie jest wyświetlany.

Wyświetl plik

@ -35,56 +35,89 @@
/**
* Resources:
* http://en.wikipedia.org/wiki/Fast_Walsh%E2%80%93Hadamard_transform
*
*
* The Fast Walsh-Hadamard Transform is a divide and conquer algorithm
* with a complexity of NlogN.
*/
/**
* We might want to use this with different types in the future.
*/
#define T int8_t
/**
* Fast Walsh-Hadamard Transform
*
* `data` an array that is the input vector. Modifed in-place
* `len` is the number of elements in `data`
* `data` an array that is the input vector. It is modifed in-place to
* become the output vector.
* `length` is the number of elements in `data`. This must be a power of two
*
* The result is returned in "hadamard" order
*/
void fwht(T *Data, size_t Len)
void fwht(int8_t *data, size_t length)
{
size_t Step, Ptr, Ptr2; T Bit1, Bit2, NewBit1, NewBit2;
for(Step=1; Step<Len; Step*=2)
{ for(Ptr=0; Ptr<Len; Ptr+=2*Step)
{ for(Ptr2=Ptr; (Ptr2-Ptr)<Step; Ptr2+=1)
{ Bit1=Data[Ptr2]; Bit2=Data[Ptr2+Step];
NewBit1=Bit2; NewBit1+=Bit1;
NewBit2=Bit2; NewBit2-=Bit1;
Data[Ptr2]=NewBit1; Data[Ptr2+Step]=NewBit2;
size_t step, decomp_index, index;
int8_t lvalue, rvalue;
/**
* The transform is decomposed into smaller WHTs.
* We ignore the normalisation factors.
*/
/* Iterate through the decompositions of size length --> 8, 4, 2 */
for (step = length / 2; step; step /= 2) {
/* Iterate through each decomposition for this step size */
for(decomp_index = 0; decomp_index < length; decomp_index += (step*2)) {
/* Interate through each DFT in the decomposition */
for(index = 0; index < step; index++) {
/* Compute a two-point Discrete Fourier Transform (DFT) */
lvalue = data[decomp_index + index];
rvalue = data[decomp_index + index + step];
/* Sum */
data[decomp_index + index] = lvalue + rvalue;
/* Difference */
data[decomp_index + index + step] = lvalue - rvalue;
}
}
}
}
/**
* Inverse Fast Hadamard Transform
* Inverse Fast Walsh-Hadamard Transform
*
* `data` an array that is the input vector. Modifed in-place
* `len` is the number of elements in `data`
* `data` an array that is the input vector. It is modifed in-place to
* become the output vector.
* `len` is the number of elements in `data`.
*
* The result is returned in "hadamard" order
*/
void ifwht(T *Data, size_t len)
void ifwht(int8_t *data, size_t length)
{
size_t step, Ptr, Ptr2; T Bit1, Bit2, NewBit1, NewBit2;
for(step=len/2; step; step/=2)
{ for(Ptr=0; Ptr<len; Ptr+=2*step)
{ for(Ptr2=Ptr; (Ptr2-Ptr)<step; Ptr2+=1)
{ Bit1=Data[Ptr2]; Bit2=Data[Ptr2+step];
NewBit1=Bit1; NewBit1-=Bit2;
NewBit2=Bit1; NewBit2+=Bit2;
Data[Ptr2]=NewBit1; Data[Ptr2+step]=NewBit2;
size_t step, decomp_index, index;
int8_t lvalue, rvalue;
/**
* The transform is decomposed into smaller WHTs.
* We ignore the normalisation factors.
*/
/* Iterate through the decompositions of size length --> 8, 4, 2 */
for (step = length / 2; step; step /= 2) {
/* Iterate through each decomposition for this step size */
for(decomp_index = 0; decomp_index < length; decomp_index += (step*2)) {
/* Interate through each IDFT in the decomposition */
for(index = 0; index < step; index++) {
/* Compute a two-point Inverse Discrete Fourier Transform (IDFT) */
lvalue = data[decomp_index + index];
rvalue = data[decomp_index + index + step];
/* Difference */
data[decomp_index + index] = lvalue - rvalue;
/* Sum */
data[decomp_index + index + step] = lvalue + rvalue;
}
}
}
@ -96,7 +129,7 @@ void ifwht(T *Data, size_t len)
int main(void)
{
T fwht_test[] = {1,0,1,0,0,1,1,0};
int8_t fwht_test[] = {1,0,1,0,0,1,1,0};
const int fwht_test_len = 8;
fwht(fwht_test, fwht_test_len);
@ -107,7 +140,7 @@ int main(void)
T ifwht_test[] = {1,0,1,0,0,1,1,0};
int8_t ifwht_test[] = {1,0,1,0,0,1,1,0};
const int ifwht_test_len = 8;
ifwht(ifwht_test, ifwht_test_len);

Wyświetl plik

@ -27,6 +27,7 @@
#include "samd20.h"
void olivia_mfsk_encode_block(char* buffer, uint8_t* tones);
void olivia_mfsk_encode_block(char* buffer, int8_t* tones);
void contestia_mfsk_encode_block(char* block, int8_t* tones);
#endif /* MFSK_H */

Wyświetl plik

@ -27,8 +27,9 @@
float si_trx_get_temperature(void);
void si_trx_on(void);
void si_trx_on(uint8_t modulation_type, float channel_spacing);
void si_trx_off(void);
void si_trx_switch_channel(uint8_t channel);
void si_trx_init(void);

Wyświetl plik

@ -26,6 +26,6 @@
#define TELEMETRY_H
uint16_t crc_checksum(char *string);
void timer0_tick_init(uint32_t frequency);
void timer0_tick_init(float frequency);
#endif /* TELEMETRY_H */

Wyświetl plik

@ -34,6 +34,7 @@
#include "system/port.h"
#include "tc/tc_driver.h"
#include "gps.h"
#include "mfsk.h"
#include "ubx_messages.h"
#include "system/wdt.h"
#include "timepulse.h"
@ -47,6 +48,10 @@
#define CALLSIGN "UBSEDSx"
/* Set the modulation mode */
//#define RTTY
#define CONTESTIA
/**
* Initialises the status LED
@ -257,6 +262,19 @@ void output_telemetry_string(void)
}
}
uint8_t started = 0;
/* We transmit 64 tones */
int8_t tones[] = {
0x1a, 0x0c, 0x07, 0x1b, 0x00, 0x13, 0x12, 0x0d,
0x12, 0x0d, 0x1f, 0x11, 0x1c, 0x1b, 0x18, 0x1e,
0x0e, 0x02, 0x0e, 0x0a, 0x05, 0x08, 0x13, 0x13,
0x1f, 0x10, 0x09, 0x0d, 0x07, 0x10, 0x1a, 0x1c,
0x0b, 0x10, 0x01, 0x0e, 0x0f, 0x19, 0x0a, 0x1d,
0x06, 0x1b, 0x0c, 0x13, 0x02, 0x0f, 0x06, 0x0c,
0x1d, 0x15, 0x17, 0x09, 0x15, 0x14, 0x1f, 0x00,
0x08, 0x06, 0x05, 0x09, 0x12, 0x13, 0x1e, 0x0a
};
/**
* MAIN
* =============================================================================
@ -289,8 +307,13 @@ int main(void)
/* Configure the Power Manager */
//powermananger_init();
/* Timer 0 for 50Hz triggering */
/* Timer 0 clocks out data */
#ifdef RTTY
timer0_tick_init(50);
#endif
#ifdef CONTESTIA
timer0_tick_init(31.25);
#endif
/**
* System initialisation
@ -304,11 +327,29 @@ int main(void)
led_init();
gps_init();
/* Initialise Si4060 */
/* Initialise Si4060 interface */
si_trx_init();
/* Start transmitting */
si_trx_on();
#ifdef RTTY
/* RTTY Mode: We modulate using the external pin */
si_trx_on(SI_MODEM_MOD_TYPE_2FSK, 0);
#endif
#ifdef CONTESTIA
/* Contestia: We switch channel to modulate */
si_trx_on(SI_MODEM_MOD_TYPE_CW, 31.25);
#endif
/* Prepare a tone sequence */
char hello[] = "HELLO";
// olivia_mfsk_encode_block(hello, tones);
contestia_mfsk_encode_block(hello, tones);
started = 1;
led_on();
@ -321,6 +362,10 @@ int main(void)
}
}
uint32_t tone_index = 0;
uint8_t binary_code;
uint8_t grey_code;
/**
* Called at 50Hz
*/
@ -329,6 +374,26 @@ void TC0_Handler(void)
if (tc_get_status(TC0) & TC_STATUS_CHANNEL_0_MATCH) {
tc_clear_status(TC0, TC_STATUS_CHANNEL_0_MATCH);
#ifdef RTTY
rtty_tick();
#endif
#ifdef CONTESTIA
if (started) {
if (tone_index < 32) {
binary_code = tones[tone_index];
grey_code = (binary_code >> 1) ^ binary_code;
si_trx_switch_channel(grey_code);
tone_index++;
} else if (tone_index > 96) {
tone_index = 0;
} else {
si_trx_state_ready();
tone_index++;
}
}
#endif
}
}

Wyświetl plik

@ -34,8 +34,8 @@
#endif
static const uint64_t ScramblingCodeOlivia = 0xE257E6D0291574ECLL;
static const uint64_t ScramblingCodeContestia = 0xEDB88320LL;
static const uint64_t scrambler_olivia = 0xE257E6D0291574ECLL;
static const uint64_t scrambler_contestia = 0xEDB88320LL;
/**
* USEFUL RESOURES =============================================================
@ -54,121 +54,113 @@ static const uint64_t ScramblingCodeContestia = 0xEDB88320LL;
* https://github.com/jamescoxon/dl-fldigi/blob/master/src/include/jalocha/pj_mfsk.h
*/
void mfsk_encode_block(char* block, int8_t* tones,
uint8_t symbols_per_block, /* The number of on-the-air symbols to transmit for each block */
uint8_t bits_per_symbol, /* The number of bits encoded in each on-the-air symbol */
uint64_t scrambler, /* Scrambler sequence */
uint8_t scrambling_shift) /* Scrambler shift */
{
int8_t fwht_vector[symbols_per_block];
memset(tones, 0, symbols_per_block * sizeof(int8_t));
/**
* There is one bit in the symbol for each character in the
* block. Iterate over each character.
*/
for (uint8_t character = 0; character < bits_per_symbol; character++) {
/* Mask off unuseds bits in the character */
block[character] &= ((symbols_per_block * 2) - 1);
/* Set a deviation in the input vector */
memset(fwht_vector, 0, symbols_per_block * sizeof(int8_t));
if (block[character] < symbols_per_block) {
fwht_vector[block[character] - 0] = 1; /* +ve */
} else {
fwht_vector[block[character] - symbols_per_block] = -1; /* -ve */
}
size_t BitsPerSymbol = 5;
size_t Symbols = 32;
/* Perform an in-place Inverse Fast Walsh-Hadamard Transform */
ifwht(fwht_vector, symbols_per_block);
size_t BitsPerCharacter = 7;
size_t SymbolsPerBlock = 64;
/* Iterate over each symbol in the output block */
for (uint32_t symbol = 0, mask_index = character * scrambling_shift;
symbol < symbols_per_block;
symbol++, mask_index++) {
uint8_t bContestia = 0;
mask_index %= symbols_per_block;
int8_t FHT_Buffer[64]; /* SymbolsPerBlock */
uint8_t OutputBlock[64]; /* SymbolsPerBlock */
/* If this bit the FWHT is significant */
if ((scrambler & (1LL << mask_index)) ?
(fwht_vector[symbol] > 0) : /* Scrambled: +ve is significant */
(fwht_vector[symbol] < 0)) { /* Not Scrambled: -ve is significant */
static const uint64_t ScramblingCode = 0xE257E6D0291574ECLL;
/* Find the bit index to set */
uint8_t rotation = symbol % bits_per_symbol;
uint8_t bit_index = (character + rotation) % bits_per_symbol;
void EncodeCharacter(uint8_t Char) {
size_t TimeBit;
uint8_t Mask = (SymbolsPerBlock << 1) - 1;
if (bContestia) {
if (Char >= 'a' && Char <= 'z')
Char += 'A' - 'a';
if (Char == ' ')
Char = 59;
else if (Char == '\r')
Char = 60;
else if (Char == '\n')
Char = 0;
else if (Char >= 33 && Char <= 90)
Char -= 32;
else if (Char == 8)
Char = 61;
else if (Char == 0)
Char = 0;
else
Char = '?' - 32;
//} else if (bRTTYM) {
} else {
Char &= Mask;
}
for (TimeBit = 0; TimeBit < SymbolsPerBlock; TimeBit++)
FHT_Buffer[TimeBit] = 0;
if (Char<SymbolsPerBlock)
FHT_Buffer[Char] = 1;
else
FHT_Buffer[Char-SymbolsPerBlock] = (-1);
ifwht(FHT_Buffer, SymbolsPerBlock);
}
void ScrambleFHT(size_t CodeOffset)
{ size_t TimeBit;
size_t CodeWrap=(SymbolsPerBlock-1);
size_t CodeBit=CodeOffset&CodeWrap;
for (TimeBit=0; TimeBit<SymbolsPerBlock; TimeBit++)
{ uint64_t CodeMask=1; CodeMask<<=CodeBit;
if (ScramblingCode&CodeMask)
FHT_Buffer[TimeBit] = (-FHT_Buffer[TimeBit]);
CodeBit+=1; CodeBit&=CodeWrap; }
}
void EncodeBlock(uint8_t *InputBlock) {
size_t FreqBit;
size_t TimeBit;
size_t nShift;
nShift = (bContestia) ? 5 : 13; // Contestia/RTTYM or Olivia
for (TimeBit = 0; TimeBit < SymbolsPerBlock; TimeBit ++)
OutputBlock[TimeBit] = 0;
for (FreqBit = 0; FreqBit < BitsPerSymbol; FreqBit++) {
EncodeCharacter(InputBlock[FreqBit]);
ScrambleFHT(FreqBit * nShift);
size_t Rotate = 0;
for (TimeBit = 0; TimeBit < SymbolsPerBlock; TimeBit++) {
if (FHT_Buffer[TimeBit] < 0) {
size_t Bit = FreqBit+Rotate;
if (Bit >= BitsPerSymbol) Bit -= BitsPerSymbol;
uint8_t Mask = 1;
Mask <<= Bit;
OutputBlock[TimeBit] |= Mask;
/* Set this bit */
tones[symbol] |= (1 << bit_index);
}
Rotate += 1;
if (Rotate >= BitsPerSymbol)
Rotate -= BitsPerSymbol;
}
}
}
/**
* This function encodes a single block of Olivia MFSK
*
* It takes a buffer of ASCII-encoded text and returns an array of
* tones to transmit.
*/
void olivia_mfsk_encode_block(char* buffer, uint8_t* tones)
void olivia_mfsk_encode_block(char* block, int8_t* tones)
{
size_t bits_per_character = 7;
size_t bits_per_symbol = 5; /* That is, there are 2^5=32 tones */
size_t symbols_per_block = 64;
/* For the moment do this */
EncodeBlock((uint8_t*)buffer);
memcpy(tones, OutputBlock, symbols_per_block);
mfsk_encode_block(block, tones, 64, bits_per_symbol, scrambler_olivia, 13);
}
/**
* This function encodes a single block of Contestia MFSK
*
* It takes a buffer of ASCII-encoded text and returns an array of
* tones to transmit.
*/
void contestia_mfsk_encode_block(char* block, int8_t* tones)
{
size_t bits_per_symbol = 5; /* That is, there are 2^5=32 tones */
for (uint8_t c_index; c_index < bits_per_symbol; c_index++) {
char character = block[c_index];
/* lowercase => UPPERCASE */
if (character >= 'a' && character <= 'z') {
character += 'A' - 'a';
}
/* Convert to contestia character set */
if (character >= '!' && character <= 'Z') { /* Printables... */
character -= 32;
} else if (character == ' ') { /* Space */
character = 59;
} else if (character == '\r') { /* Carriage Return */
character = 60;
} else if (character == '\n') { /* Line Feed */
character = 0;
} else if (character == 8) { /* Backspace */
character = 61;
} else if (character == 0) { /* Null */
character = 0;
} else { /* ???????????? */
character = '?' - 32;
}
block[c_index] = character;
}
mfsk_encode_block(block, tones, 32, bits_per_symbol, scrambler_contestia, 5);
}
#ifdef OLIVIA_MFSK_ENCODE_TEST
void main(void)

Wyświetl plik

@ -162,6 +162,20 @@ static void si_trx_set_gpio_configuration(si_gpio_t gpio0, si_gpio_t gpio1,
_si_trx_transfer(8, 0, buffer);
}
/**
* Starts transmitting
*/
static void si_trx_start_tx(uint8_t channel)
{
uint8_t buffer[5];
buffer[0] = SI_CMD_START_TX;
buffer[1] = channel;
buffer[2] = (1 << 4);
buffer[3] = 0;
buffer[4] = 0;
_si_trx_transfer(5, 0, buffer);
}
/**
* Gets readings from the auxillary ADC
*/
@ -271,7 +285,7 @@ static void si_trx_set_tx_pa_duty_cycle(uint8_t pa_duty_cycle)
/**
* Set the synthesiser to the given frequency
*/
static void si_trx_set_frequency(uint32_t frequency)
static void si_trx_set_frequency(uint32_t frequency, float channel_spacing)
{
uint8_t outdiv, band;
@ -300,25 +314,34 @@ static void si_trx_set_frequency(uint32_t frequency)
uint32_t m = (uint32_t)(rest * (float)(1 << 19));
/* Set the modem deviation, in units of the VCO resolution */
float dev_ratio = (float)RF_DEVIATION / (float)f_pfd;
uint32_t dev = (uint32_t)(dev_ratio * (float)(1 << 19));
/* Set the channel spacing, in units of the VCO resolution */
float channel_spacing_ratio = channel_spacing / (float)f_pfd;
uint32_t channel_step = (uint32_t)(channel_spacing_ratio * (float)(1 << 19));
/* Set the frac-n PLL output divider */
si_trx_frequency_control_set_band(band);
/* Set the frac-n PLL divisior */
si_trx_frequency_control_set_divider(n, m);
/* Set the channel step in the PLL */
si_trx_frequency_control_set_channel_step_size(channel_step);
/* Set the frequency deviation in the modem */
si_trx_modem_set_deviation(dev);
//const uint8_t set_frequency_control_inte[] = {0x11, 0x40, 0x08, 0x00, n, m2, m1, m0, 0x0B, 0x61, 0x20, 0xFA};
}
/**
* Resets the transceiver
*/
void si_trx_reset(void)
void si_trx_reset(uint8_t modulation_type, float channel_spacing)
{
_si_trx_sdn_enable(); /* active high shutdown = reset */
@ -345,14 +368,14 @@ void si_trx_reset(void)
SI_GPIO_PIN_CFG_GPIO_MODE_INPUT | SI_GPIO_PIN_CFG_PULL_ENABLE,
SI_GPIO_PIN_CFG_DRV_STRENGTH_LOW);
si_trx_set_frequency(RADIO_FREQUENCY);
si_trx_set_frequency(RADIO_FREQUENCY, channel_spacing);
si_trx_set_tx_power(RADIO_POWER);
/* RTTY from GPIO1 */
si_trx_modem_set_modulation(SI_MODEM_MOD_DIRECT_MODE_ASYNC,
SI_MODEM_MOD_GPIO_1,
SI_MODEM_MOD_SOURCE_DIRECT,
SI_MODEM_MOD_TYPE_2FSK);
modulation_type);
si_trx_state_tx_tune();
}
@ -360,10 +383,10 @@ void si_trx_reset(void)
/**
* Enables the radio and starts transmitting
*/
void si_trx_on(void)
void si_trx_on(uint8_t modulation_type, float channel_spacing)
{
si_trx_reset();
si_trx_state_tx();
si_trx_reset(modulation_type, channel_spacing);
si_trx_start_tx(1);
}
/**
* Disables the radio and places it in shutdown
@ -373,6 +396,14 @@ void si_trx_off(void)
si_trx_state_ready();
_si_trx_sdn_enable();
}
/**
* Switches the transmittion to the specified channel
*/
void si_trx_switch_channel(uint8_t channel)
{
si_trx_state_ready();
si_trx_start_tx(channel);
}
/**
* Initialises the radio interface to the radio

Wyświetl plik

@ -75,11 +75,11 @@ uint16_t crc_checksum(char *string)
/**
* Initialises a timer interupt at the given frequency
*/
void timer0_tick_init(uint32_t frequency)
void timer0_tick_init(float frequency)
{
/* Calculate the wrap value for the given frequency */
uint32_t gclk0_frequency = system_gclk_chan_get_hz(0);
uint32_t count = gclk0_frequency / frequency;
float gclk0_frequency = (float)system_gclk_chan_get_hz(0);
uint32_t count = (uint32_t)(gclk0_frequency / frequency);
/* Configure Timer 0 */
bool t0_capture_channel_enables[] = {false, false};