/*
 * 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++;
    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
        } 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)

                    periods = 200 << 24; //dds off 1 dot length spacing
                    delay(tempo); // between symbols in a character
                }
            }
            periods = 200 << 24; //dds off 3 dots length spacing
            delay(tempo * 3); // between characters in a word
        }
        c = *stringCw++; // Next caracter in string
    }
    periods = 200 << 24; //dds off 
}
void Modulation::sendCarriage(bool state) {
    if (state) {
        periods = cwfr;
    } else {
        periods = 200 << 24;
    }
}


Modulation* Modulation::anchor = NULL;
uint32_t Modulation::period_us;

