361 lines
13 KiB
C++
361 lines
13 KiB
C++
|
|
/*
|
||
|
|
* To change this license header, choose License Headers in Project Properties.
|
||
|
|
* To change this template file, choose Tools | Templates
|
||
|
|
* and open the template in the editor.
|
||
|
|
*/
|
||
|
|
|
||
|
|
/*
|
||
|
|
* File: Modulation.cpp
|
||
|
|
* Author: ale
|
||
|
|
*
|
||
|
|
* Created on 28 août 2025, 07:55
|
||
|
|
*/
|
||
|
|
|
||
|
|
#include "Modulation.h"
|
||
|
|
|
||
|
|
Modulation::Modulation(config* _cfg) : cfg(_cfg) {
|
||
|
|
setFreqWspr(cfg->freq+cfg->offset);
|
||
|
|
setFreqRtty(cfg->freq+cfg->offset+500L); //a cote de la fréquence wspr
|
||
|
|
setFreqFt8(cfg->freq+cfg->offset+500L);
|
||
|
|
setFreqCW(cfg->freq+cfg->offset+500L);
|
||
|
|
anchor = this;
|
||
|
|
pinMode(TEST_PIN,OUTPUT);
|
||
|
|
}
|
||
|
|
|
||
|
|
Modulation::Modulation(const Modulation& orig) {
|
||
|
|
}
|
||
|
|
|
||
|
|
Modulation::~Modulation() {
|
||
|
|
}
|
||
|
|
|
||
|
|
void Modulation::marshall() {
|
||
|
|
// Clear the alarm irq
|
||
|
|
hw_clear_bits(&timer_hw->intr, 1u << ALARM_NUM);
|
||
|
|
// Reset the alarm register
|
||
|
|
timer_hw->alarm[ALARM_NUM] = timer_hw->timerawl + period_us ;
|
||
|
|
anchor->timerCallback();
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
void Modulation::startTimerIRQ(uint32_t us) {
|
||
|
|
|
||
|
|
period_us = us;
|
||
|
|
// Enable the interrupt for the alarm (we're using Alarm 0)
|
||
|
|
hw_set_bits(&timer_hw->inte, 1u << ALARM_NUM) ;
|
||
|
|
// Associate an interrupt handler with the ALARM_IRQ
|
||
|
|
irq_set_exclusive_handler(ALARM_IRQ, marshall) ;
|
||
|
|
// Enable the alarm interrupt
|
||
|
|
irq_set_enabled(ALARM_IRQ, true) ;
|
||
|
|
// Write the lower 32 bits of the target time to the alarm register, arming it.
|
||
|
|
timer_hw->alarm[ALARM_NUM] = timer_hw->timerawl + period_us ;
|
||
|
|
irq_set_priority(ALARM_IRQ, 0); // Priorité haute
|
||
|
|
|
||
|
|
Serial.println("init irq");
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
void Modulation::stopTimerIRQ() {
|
||
|
|
irq_set_enabled(ALARM_IRQ, false);
|
||
|
|
hardware_alarm_unclaim(ALARM_NUM);
|
||
|
|
Serial.println("stop irq");
|
||
|
|
}
|
||
|
|
|
||
|
|
void Modulation::timerCallback() {
|
||
|
|
gpio_xor_mask(1u << TEST_PIN); // Toggle rapide
|
||
|
|
flagIrq = true;
|
||
|
|
//Serial.print("irq");
|
||
|
|
}
|
||
|
|
|
||
|
|
void Modulation::waitIrq(){
|
||
|
|
while (!flagIrq){}
|
||
|
|
flagIrq=false;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
void Modulation::sendWspr(char *locator) {
|
||
|
|
|
||
|
|
memset(txBuffer, 0, WSPR_SYMBOL_COUNT);
|
||
|
|
if (cfg->follow) {
|
||
|
|
jtencode.wspr_encode(cfg->call, locator, dbm, txBuffer); //avec suivi
|
||
|
|
} else {
|
||
|
|
jtencode.wspr_encode(cfg->call, locator, cfg->dbm, txBuffer); //sans suivi
|
||
|
|
}
|
||
|
|
|
||
|
|
startTimerIRQ(WSPR_DELAY * 1000);
|
||
|
|
flagIrq = false;
|
||
|
|
for (uint8_t i = 0; i < WSPR_SYMBOL_COUNT; i++) {
|
||
|
|
periods = wsfr[txBuffer[i]]; //modulation fsk sur 4 niveaux de fréquences avec un shift de 1.48 Hz le contenu de buffer est 0 ou 1 ou 2 ou 3
|
||
|
|
//delay(WSPR_DELAY);
|
||
|
|
waitIrq();
|
||
|
|
}
|
||
|
|
periods = 200 << 24; //dds off
|
||
|
|
stopTimerIRQ();
|
||
|
|
}
|
||
|
|
|
||
|
|
void Modulation::setDbm(uint8_t _dbm){
|
||
|
|
dbm=_dbm;
|
||
|
|
}
|
||
|
|
|
||
|
|
void Modulation::sendRtty(char* stringRtty) {
|
||
|
|
const static int TableRtty[59] PROGMEM = { 4, 22, 17, 5, 18, 0, 11, 26, 30, 9, 0, 0, 6, 24, 7, 23, 13, 29, 25, 16, 10, 1, 21, 28, 12, 3, 14, 15, 0, 0, 0, 19, 0, 24, 19, 14, 18, 16, 22, 11, 5, 12, 26, 30, 9, 7, 6, 3, 13, 29, 10, 20, 1, 28, 15, 25, 23, 21, 17 };
|
||
|
|
//tableau dans l'ordre ascii qui démarre à partir de l'espace 32
|
||
|
|
//les valeurs représentent le code baudot
|
||
|
|
int signlett = 1; // RTTY Baudot signs/letters tables toggle
|
||
|
|
char c;
|
||
|
|
startTimerIRQ(RTTY_DELAY*1000);
|
||
|
|
c = *stringRtty++;
|
||
|
|
for (int n = 1; n < 20; n++) { //sync on envoie une série de letters table code
|
||
|
|
rttyTxByte(27);
|
||
|
|
}
|
||
|
|
|
||
|
|
while (c != '\0') {
|
||
|
|
//Serial.print(c);
|
||
|
|
c = toupper(int(c)); // Uppercase
|
||
|
|
if (c == 10) // Line Feed
|
||
|
|
{
|
||
|
|
rttyTxByte(8);
|
||
|
|
} else if (c == 13) // Carriage Return
|
||
|
|
{
|
||
|
|
rttyTxByte(2);
|
||
|
|
} else if (c == 32) // Space
|
||
|
|
{
|
||
|
|
rttyTxByte(4);
|
||
|
|
} else if (c > 32 && c < 91) {
|
||
|
|
c = c - 32;
|
||
|
|
if (c < 33) {
|
||
|
|
if (signlett == 1) {
|
||
|
|
signlett = 0; // toggle form signs to letters table
|
||
|
|
rttyTxByte(27); //
|
||
|
|
}
|
||
|
|
} else if (signlett == 0) {
|
||
|
|
signlett = 1; // toggle form letters to signs table
|
||
|
|
rttyTxByte(31); //
|
||
|
|
}
|
||
|
|
rttyTxByte(int(pgm_read_word(&TableRtty[int(c)]))); // Send the 5 bits word
|
||
|
|
}
|
||
|
|
c = *stringRtty++; // Next character in string
|
||
|
|
}
|
||
|
|
periods = 200 << 24; //dds off
|
||
|
|
stopTimerIRQ();
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
void Modulation::rttyTxByte(char c) {
|
||
|
|
int val;
|
||
|
|
c = (c << 2) + 3; //ajoute les deux bits de stop
|
||
|
|
for (int b = 7; b >= 0; b--) // MSB first
|
||
|
|
{
|
||
|
|
val = bitRead(c, b); // Read 1 bit
|
||
|
|
if (val == 0) periods = rttyfr[0]; //modulation fsk
|
||
|
|
else periods = rttyfr[1];
|
||
|
|
//delay(RTTY_DELAY);
|
||
|
|
waitIrq();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void Modulation::sendFt8(char *call, char *locator) {
|
||
|
|
char buf[20];
|
||
|
|
sprintf(buf, "%s%s", call, locator);
|
||
|
|
//Serial.println(buf);
|
||
|
|
memset(txBuffer, 0, FT8_SYMBOL_COUNT);
|
||
|
|
jtencode.ft8_encode(buf, txBuffer);
|
||
|
|
|
||
|
|
//debug
|
||
|
|
/*
|
||
|
|
int n, lf;
|
||
|
|
lf = 0;
|
||
|
|
for (n = 0; n < FT8_SYMBOL_COUNT; n++) { //print symbols on serial monitor
|
||
|
|
if (lf % 16 == 0) {
|
||
|
|
Serial.println();
|
||
|
|
Serial.print(n);
|
||
|
|
Serial.print(": ");
|
||
|
|
}
|
||
|
|
lf++;
|
||
|
|
Serial.print(txBuffer[n]);
|
||
|
|
Serial.print(',');
|
||
|
|
}
|
||
|
|
Serial.println();
|
||
|
|
*/
|
||
|
|
startTimerIRQ(FT8_DELAY*1000);
|
||
|
|
flagIrq=false;
|
||
|
|
for (uint8_t i = 0; i < FT8_SYMBOL_COUNT; i++) {
|
||
|
|
periods = ft8fr[txBuffer[i]];
|
||
|
|
//delay(FT8_DELAY);
|
||
|
|
waitIrq();
|
||
|
|
|
||
|
|
}
|
||
|
|
periods = 200 << 24; //dds off
|
||
|
|
stopTimerIRQ();
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
/***********************************************************************************
|
||
|
|
* Hellschreiber 122.5 bauds => 8,163ms
|
||
|
|
* http://brainwagon.org/2012/01/11/hellduino-sending-hellschreiber-from-an-arduino
|
||
|
|
************************************************************************************/
|
||
|
|
|
||
|
|
void Modulation::sendHell(char * stringHell) {
|
||
|
|
static word GlyphTab[59][8] PROGMEM = {
|
||
|
|
{0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
|
||
|
|
{0x1f9c, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
|
||
|
|
{0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
|
||
|
|
{0x0330, 0x0ffc, 0x0330, 0x0ffc, 0x0330, 0x0000, 0x0000},
|
||
|
|
{0x078c, 0x0ccc, 0x1ffe, 0x0ccc, 0x0c78, 0x0000, 0x0000},
|
||
|
|
{0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
|
||
|
|
{0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
|
||
|
|
{0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
|
||
|
|
{0x01e0, 0x0738, 0x1c0e, 0x0000, 0x0000, 0x0000, 0x0000},
|
||
|
|
{0x1c0e, 0x0738, 0x01e0, 0x0000, 0x0000, 0x0000, 0x0000},
|
||
|
|
{0x018c, 0x0198, 0x0ff0, 0x0198, 0x018c, 0x0000, 0x0000},
|
||
|
|
{0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
|
||
|
|
{0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
|
||
|
|
{0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
|
||
|
|
{0x001c, 0x001c, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
|
||
|
|
{0x001c, 0x0070, 0x01c0, 0x0700, 0x1c00, 0x0000, 0x0000},
|
||
|
|
{0x07f8, 0x0c0c, 0x0c0c, 0x0c0c, 0x07f8, 0x0000, 0x0000},
|
||
|
|
{0x0300, 0x0600, 0x0ffc, 0x0000, 0x0000, 0x0000, 0x0000},
|
||
|
|
{0x061c, 0x0c3c, 0x0ccc, 0x078c, 0x000c, 0x0000, 0x0000},
|
||
|
|
{0x0006, 0x1806, 0x198c, 0x1f98, 0x00f0, 0x0000, 0x0000},
|
||
|
|
{0x1fe0, 0x0060, 0x0060, 0x0ffc, 0x0060, 0x0000, 0x0000},
|
||
|
|
{0x000c, 0x000c, 0x1f8c, 0x1998, 0x18f0, 0x0000, 0x0000},
|
||
|
|
{0x07fc, 0x0c66, 0x18c6, 0x00c6, 0x007c, 0x0000, 0x0000},
|
||
|
|
{0x181c, 0x1870, 0x19c0, 0x1f00, 0x1c00, 0x0000, 0x0000},
|
||
|
|
{0x0f3c, 0x19e6, 0x18c6, 0x19e6, 0x0f3c, 0x0000, 0x0000},
|
||
|
|
{0x0f80, 0x18c6, 0x18cc, 0x18cc, 0x0ff0, 0x0000, 0x0000},
|
||
|
|
{0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
|
||
|
|
{0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
|
||
|
|
{0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
|
||
|
|
{0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
|
||
|
|
{0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
|
||
|
|
{0x1800, 0x1800, 0x19ce, 0x1f00, 0x0000, 0x0000, 0x0000},
|
||
|
|
{0x01f0, 0x0208, 0x04e4, 0x0514, 0x0514, 0x03e0, 0x0000},
|
||
|
|
{0x07fc, 0x0e60, 0x0c60, 0x0e60, 0x07fc, 0x0000, 0x0000},
|
||
|
|
{0x0c0c, 0x0ffc, 0x0ccc, 0x0ccc, 0x0738, 0x0000, 0x0000},
|
||
|
|
{0x0ffc, 0x0c0c, 0x0c0c, 0x0c0c, 0x0c0c, 0x0000, 0x0000},
|
||
|
|
{0x0c0c, 0x0ffc, 0x0c0c, 0x0c0c, 0x07f8, 0x0000, 0x0000},
|
||
|
|
{0x0ffc, 0x0ccc, 0x0ccc, 0x0c0c, 0x0c0c, 0x0000, 0x0000},
|
||
|
|
{0x0ffc, 0x0cc0, 0x0cc0, 0x0c00, 0x0c00, 0x0000, 0x0000},
|
||
|
|
{0x0ffc, 0x0c0c, 0x0c0c, 0x0ccc, 0x0cfc, 0x0000, 0x0000},
|
||
|
|
{0x0ffc, 0x00c0, 0x00c0, 0x00c0, 0x0ffc, 0x0000, 0x0000},
|
||
|
|
{0x0c0c, 0x0c0c, 0x0ffc, 0x0c0c, 0x0c0c, 0x0000, 0x0000},
|
||
|
|
{0x003c, 0x000c, 0x000c, 0x000c, 0x0ffc, 0x0000, 0x0000},
|
||
|
|
{0x0ffc, 0x00c0, 0x00e0, 0x0330, 0x0e1c, 0x0000, 0x0000},
|
||
|
|
{0x0ffc, 0x000c, 0x000c, 0x000c, 0x000c, 0x0000, 0x0000},
|
||
|
|
{0x0ffc, 0x0600, 0x0300, 0x0600, 0x0ffc, 0x0000, 0x0000},
|
||
|
|
{0x0ffc, 0x0700, 0x01c0, 0x0070, 0x0ffc, 0x0000, 0x0000},
|
||
|
|
{0x0ffc, 0x0c0c, 0x0c0c, 0x0c0c, 0x0ffc, 0x0000, 0x0000},
|
||
|
|
{0x0c0c, 0x0ffc, 0x0ccc, 0x0cc0, 0x0780, 0x0000, 0x0000},
|
||
|
|
{0x0ffc, 0x0c0c, 0x0c3c, 0x0ffc, 0x000f, 0x0000, 0x0000},
|
||
|
|
{0x0ffc, 0x0cc0, 0x0cc0, 0x0cf0, 0x079c, 0x0000, 0x0000},
|
||
|
|
{0x078c, 0x0ccc, 0x0ccc, 0x0ccc, 0x0c78, 0x0000, 0x0000},
|
||
|
|
{0x0c00, 0x0c00, 0x0ffc, 0x0c00, 0x0c00, 0x0000, 0x0000},
|
||
|
|
{0x0ff8, 0x000c, 0x000c, 0x000c, 0x0ff8, 0x0000, 0x0000},
|
||
|
|
{0x0ffc, 0x0038, 0x00e0, 0x0380, 0x0e00, 0x0000, 0x0000},
|
||
|
|
{0x0ff8, 0x000c, 0x00f8, 0x000c, 0x0ff8, 0x0000, 0x0000},
|
||
|
|
{0x0e1c, 0x0330, 0x01e0, 0x0330, 0x0e1c, 0x0000, 0x0000},
|
||
|
|
{0x0e00, 0x0380, 0x00fc, 0x0380, 0x0e00, 0x0000, 0x0000},
|
||
|
|
{0x0c1c, 0x0c7c, 0x0ccc, 0x0f8c, 0x0e0c, 0x0000, 0x0000}
|
||
|
|
};
|
||
|
|
int val;
|
||
|
|
char ch;
|
||
|
|
word fbits;
|
||
|
|
startTimerIRQ(HELL_DELAY);
|
||
|
|
ch = *stringHell++;
|
||
|
|
while (ch != '\0') {
|
||
|
|
ch = toupper(int(ch)); // Uppercase
|
||
|
|
if (ch >= 32 && ch <= 90) // Character is in the range of ASCII space to Z
|
||
|
|
{
|
||
|
|
ch -= 32; // Character number starting at 0
|
||
|
|
for (int i = 0; i < 7; i++) // Scanning each 7 columns of glyph
|
||
|
|
{
|
||
|
|
fbits = int(pgm_read_word(&GlyphTab[int(ch)][i])); // Get each column of glyph
|
||
|
|
for (int b = 0; b < 14; b++) // Scanning each 14 rows
|
||
|
|
{
|
||
|
|
val = bitRead(fbits, b); // Get binary state of pixel
|
||
|
|
//DDS.setfreq(freqHell * val, 0); // Let's transmit (or not if pixel is clear)
|
||
|
|
if (val){
|
||
|
|
periods = rttyfr[0]; //utiliser la fréquence du rtty pour essais
|
||
|
|
}
|
||
|
|
else{
|
||
|
|
periods = 200 << 24; //dds off
|
||
|
|
}
|
||
|
|
waitIrq();
|
||
|
|
//delayMicroseconds(HELL_DELAY); //8,163ms/2
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
ch = *stringHell++; // Next character in string
|
||
|
|
}
|
||
|
|
periods = 200 << 24; //dds off
|
||
|
|
stopTimerIRQ();
|
||
|
|
}
|
||
|
|
|
||
|
|
/***************************************************************************
|
||
|
|
* CW
|
||
|
|
***************************************************************************/
|
||
|
|
void Modulation::sendCw(char * stringCw, int cwWpm) {
|
||
|
|
static int morseVaricode[2][59] PROGMEM = {
|
||
|
|
{0, 212, 72, 0, 144, 0, 128, 120, 176, 180, 0, 80, 204, 132, 84, 144, 248, 120, 56, 24, 8, 0, 128, 192, 224, 240, 224, 168, 0, 136, 0, 48, 104, 64, 128, 160, 128, 0, 32, 192, 0, 0, 112, 160, 64, 192, 128, 224, 96, 208, 64, 0, 128, 32, 16, 96, 144, 176, 192},
|
||
|
|
{7, 6, 5, 0, 4, 0, 4, 6, 5, 6, 0, 5, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 0, 5, 0, 6, 6, 2, 4, 4, 3, 1, 4, 3, 4, 2, 4, 3, 4, 2, 2, 3, 4, 4, 3, 3, 1, 3, 4, 3, 4, 4, 4}
|
||
|
|
};
|
||
|
|
|
||
|
|
int tempo = 1200 / cwWpm; // Duration of 1 dot
|
||
|
|
byte nb_bits, val;
|
||
|
|
int d;
|
||
|
|
int c = *stringCw++;
|
||
|
|
startTimerIRQ(tempo*1000);
|
||
|
|
waitIrq();
|
||
|
|
while (c != '\0') {
|
||
|
|
c = toupper(c); // Uppercase
|
||
|
|
if (c == 32) { // Space character between words in string
|
||
|
|
periods = 200 << 24; //dds off 7 dot length spacing
|
||
|
|
//delay(tempo * 7); // between words
|
||
|
|
for (int t=0;t<7;t++) waitIrq();
|
||
|
|
} else if (c > 32 && c < 91) {
|
||
|
|
c = c - 32;
|
||
|
|
d = int(pgm_read_word(&morseVaricode[0][c])); // Get CW varicode
|
||
|
|
nb_bits = int(pgm_read_word(&morseVaricode[1][c])); // Get CW varicode length
|
||
|
|
if (nb_bits != 0) { // Number of bits = 0 -> invalid character #%<>
|
||
|
|
for (int b = 7; b > 7 - nb_bits; b--) { // Send CW character, each bit represents a symbol (0 for dot, 1 for dash) MSB first
|
||
|
|
val = bitRead(d, b); //look varicode
|
||
|
|
|
||
|
|
periods = cwfr;
|
||
|
|
|
||
|
|
//delay(tempo + 2 * tempo * val); // A dot length or a dash length (3 times the dot)
|
||
|
|
waitIrq();
|
||
|
|
if (val == 1) {
|
||
|
|
waitIrq();
|
||
|
|
waitIrq();
|
||
|
|
}
|
||
|
|
periods = 200 << 24; //dds off 1 dot length spacing
|
||
|
|
//delay(tempo); // between symbols in a character
|
||
|
|
waitIrq();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
periods = 200 << 24; //dds off 3 dots length spacing
|
||
|
|
//delay(tempo * 3); // between characters in a word
|
||
|
|
waitIrq();
|
||
|
|
waitIrq();
|
||
|
|
waitIrq();
|
||
|
|
}
|
||
|
|
c = *stringCw++; // Next caracter in string
|
||
|
|
}
|
||
|
|
periods = 200 << 24; //dds off
|
||
|
|
stopTimerIRQ();
|
||
|
|
}
|
||
|
|
void Modulation::sendCarriage(bool state) {
|
||
|
|
if (state) {
|
||
|
|
periods = cwfr;
|
||
|
|
} else {
|
||
|
|
periods = 200 << 24;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
Modulation* Modulation::anchor = NULL;
|
||
|
|
uint32_t Modulation::period_us;
|
||
|
|
|