#include "wspr.h"

#include <ctype.h>

#include <util/delay.h>
#include <avr/pgmspace.h>
#include <string.h>



const uint8_t WSPR_SYNC[WSPR_LENGTH] PROGMEM = {
	1,1,0,0,0,0,0,0,1,0,0,0,1,1,1,0,0,0,1,0,0,1,0,1,1,1,1,0,0,0,0,0,0,0,1,0,0,1,0,1,0,0,
	0,0,0,0,1,0,1,1,0,0,1,1,0,1,0,0,0,1,1,0,1,0,0,0,0,1,1,0,1,0,1,0,1,0,1,0,0,1,0,0,1,0,
	1,1,0,0,0,1,1,0,1,0,1,0,0,0,1,0,0,0,0,0,1,0,0,1,0,0,1,1,1,0,1,1,0,0,1,1,0,1,0,0,0,1,
	1,1,0,0,0,0,0,1,0,1,0,0,1,1,0,0,0,0,0,0,0,1,1,0,1,0,1,1,0,0,0,1,1,0.0,0
};

static inline uint8_t wspr_call_char(char c) {
	c = toupper(c);
	if(c >= '0' && c <= '9') {
		return c - '0';
	}
	else if(c >= 'A' && c <= 'Z') {
		return 10 + (c - 'A');
	}
	// Turn everything else to space
	else {
		return 36;
	}
}

static inline uint8_t wspr_loc_char(char c) {
	c = toupper(c);
	if(c >= '0' && c <= '9') {
		return c - '0';
	}
	else if(c >= 'A' && c <= 'R') {
		return c - 'A';
	}
	// Everthing else becomes 9/I
	else {
		return 9;
	}
}

static inline uint8_t wspr_power(uint8_t power) {
	if(power > 60) {
		return 60;
	}
	return power;
}

const uint8_t WSPR_POWER_LOC_BITS = 22;
uint64_t wspr_message(char *call, char *loc, uint8_t power) {
	uint32_t m, n;

	n = wspr_call_char(call[0]);
	n = n * 36 + wspr_call_char(call[1]);
	n = n * 10 + wspr_call_char(call[2]);
	n = n * 27 + wspr_call_char(call[3]) - 10;
	n = n * 27 + wspr_call_char(call[4]) - 10;
	n = n * 27 + wspr_call_char(call[5]) - 10;

	m = 179 - 10 * wspr_loc_char(loc[0]) - wspr_loc_char(loc[2]);
	m = m * 180 + 10 * wspr_loc_char(loc[1]) + wspr_loc_char(loc[3]);
	m = m * 128 + wspr_power(power) + 64;

	return ((uint64_t) n) << WSPR_POWER_LOC_BITS | m;
}


const uint32_t WSPR_FEC_TAPS1 = 0xF2D05351;
const uint32_t WSPR_FEC_TAPS2 = 0xE4613C47;

uint8_t wspr_parity32(uint32_t x) {
	x ^= x >> 16;
    x ^= x >> 8;
    x ^= x >> 4;
    x ^= x >> 2;
    x ^= x >> 1;
	return x & 1;
}

void wspr_fec(uint64_t msg, uint8_t *output) {
	memset(output, 0, WSPR_LENGTH);

	uint32_t reg = 0;
	for(uint8_t i = 0;  i < 81; i++) {
		uint8_t bit = 0;
		if(i < 50) {
			bit = (msg >> (50 - i - 1)) & 1;
		}

		reg = (reg << 1) | bit;

		uint32_t tabs1 = reg & WSPR_FEC_TAPS1;
		uint32_t tabs2 = reg & WSPR_FEC_TAPS2;

		output[i * 2] = wspr_parity32(tabs1);
		output[i * 2 + 1] = wspr_parity32(tabs2);
	}
}

uint8_t wspr_reverse_bits(uint8_t bits) {
	//https://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/AVR-Built_002din-Functions.html
	return __builtin_avr_insert_bits (0x01234567, bits, 0);
}


void wspr_interleave(uint8_t *input, uint8_t *output) {
	uint8_t p = 0;
	uint8_t i = 0;
	while(p < WSPR_LENGTH) {
		uint8_t rev_i = wspr_reverse_bits(i);
		if(rev_i < WSPR_LENGTH) {
			output[rev_i] = input[p];
			p++;
		}
		i++;
	}
}

void wspr_add_sync(uint8_t *symbols) {
	for(uint8_t i = 0; i < WSPR_LENGTH; i++) {
		symbols[i] = pgm_read_byte(&(WSPR_SYNC[i])) + 2 * symbols[i];
	}
}


void wspr_encode(char *call, char *loc, uint8_t power, uint8_t *buffer) {
	uint8_t tmp_buffer[WSPR_LENGTH];

	uint64_t msg = wspr_message(call, loc, power);
	wspr_fec(msg, tmp_buffer);
	wspr_interleave(tmp_buffer, buffer);
	wspr_add_sync(buffer);
}



// For PLL with 800MHz
const struct si5351_params WSPR_SYMBOLS[4] = {
	// 7040100.000000: 113 + 44687 / 70401
	// actual: 7040100.000000
	{14033, 17455, 70401},
	// 7040101.464844: 113 + 447801 / 705503
	// actual: 7040101.464844
	{14033, 172785, 705503},
	// 7040102.929688: 113 + 38515 / 60682
	// actual: 7040102.929688
	{14033, 14678, 60682},
	// 7040104.394531: 113 + 123233 / 194166
	// actual: 7040104.394531
	{14033, 46378, 194166},
};


const uint16_t WSPR_PERIOD = 683;

void wspr_transmit(enum si5351_multisynth synth, uint8_t *msg) {
	si5351_ms_enable_output(synth);
	for(uint8_t i = 0; i < WSPR_LENGTH; i++) {
		uint8_t sym = msg[i];
		si5351_ms_write_params(synth, WSPR_SYMBOLS[sym]);
		_delay_ms(WSPR_PERIOD);
	}
	si5351_ms_disable_output(synth);
}
