Amplitude Modulation (AM) SDR TX for Teensy 4.X code request

stefanodi

Member
Hello everybody. A friend told me I was wrong and they didn't understand the question in my previous post.
I write my request for help in another way:
is it possible to have a code for Teensy 4.X to build a TX SDR in amplitude modulated (AM)?
Thanks in advance for those who will have the kindness to help me.
Stefano.
 
The Teensy Audio library has sine wave generators and multipliers, the combination of which does AM. This is in 16-bit integer. See the Teensy Audio Design Tool.

Alternatively, this can be done in floating point where there is an explicit amplitude modulator that includes a sine wave generator. See the corresponding F32 Design Tool. In general, the floating point library has more SDR related classes whereas the integer library has more music related ones.
 
The Teensy Audio library has sine wave generators and multipliers, the combination of which does AM. This is in 16-bit integer. See the Teensy Audio Design Tool.

Alternatively, this can be done in floating point where there is an explicit amplitude modulator that includes a sine wave generator. See the corresponding F32 Design Tool. In general, the floating point library has more SDR related classes whereas the integer library has more music related ones.


Thanks Bob for the reply.
The project is based on an RTX type I & Q mixer card.
I would like to build an "old style" RTX, without many controls and easy to build, but ALL SDR.
My problem is generating the modulated amplitude.
If you can help me I will be grateful.

I have already tested the OpenAudio Arduino Library and I think it will be my next project as soon as this is finished.
Thank you and good day!
Stefano

Attached is the photo of the project only for the TX part:
txparts.jpg


Code:
// GUItool: begin automatically generated code
AudioInputI2S            audioInput;     //xy=135.88888549804688,347.0000457763672
AudioFilterFIR           TX_hilbert_45;  //xy=356.8888931274414,499.00008392333984
AudioFilterFIR           TX_hilbert_m45; //xy=359.88893127441406,230.0000123977661

AudioSynthWaveform       15Khz_Sine_Gen;      //

AudioFilterFIR           fir1;           //

AudioEffectMultiply      multiply1;      //

AudioFilterFIR           fir2;           //

AudioMixer4              Q_Switch; //xy=943.8887786865234,515.0000038146973
AudioMixer4              I_Switch; //xy=952.8888320922852,252.00001907348633
AudioOutputI2S           audioOutput;    //xy=1180.888816833496,388.9999723434448
AudioConnection          patchCord1(audioInput, 0, TX_hilbert_m45, 0);
AudioConnection          patchCord2(audioInput, 1, TX_hilbert_45, 0);
AudioConnection          patchCord3(TX_hilbert_45, 0, Q_Switch, 0);
AudioConnection          patchCord4(TX_hilbert_m45, 0, I_Switch, 0);

AudioConnection          patchCord5(15Khz_Sine_Gen, 0, multiply1, 1);
AudioConnection          patchCord6(fir1, 0, multiply1, 0);
AudioConnection          patchCord7(multiply1, fir2);

AudioConnection          patchCord8(fir2, 0, I_Switch, 1);

AudioConnection          patchCord9(Q_Switch, 0, audioOutput, 1);
AudioConnection          patchCord10(I_Switch, 0, audioOutput, 0);
AudioControlSGTL5000     audioShield;    //xy=346.8889274597168,134.99999952316284
// GUItool: end automatically generated code
 
Hi Stefanodi - What you have produces double sideband suppressed carrier. You need to add in a constant carrier level, like 1.0. This is like vout = [1 + m(t)]*sin2*pi*15000*t wher m(t) is the modulating waveform that you have as a sine wave. Something like

Code:
AudioSynthWaveformDc     dc1;            //xy=71,634
AudioInputI2S            i2s2;           //xy=76,754
AudioSynthWaveform       waveform1;      //xy=78,688
AudioFilterFIR           fir1;           //xy=219,754
AudioMixer4              mixer1;         //xy=220,670
AudioEffectMultiply      multiply1;      //xy=359,699
AudioOutputI2S           i2s1;           //xy=449,759
AudioConnection          patchCord1(dc1, 0, mixer1, 0);
AudioConnection          patchCord2(i2s2, 0, fir1, 0);
AudioConnection          patchCord3(waveform1, 0, mixer1, 1);
AudioConnection          patchCord4(fir1, 0, multiply1, 1);
AudioConnection          patchCord5(mixer1, 0, multiply1, 0);
AudioConnection          patchCord6(multiply1, 0, i2s1, 0);
AudioControlSGTL5000     sgtl5000_1;     //xy=230,821

The mixer does the addition of the 1 and the modulating waveform coming from the ADC. This is lacking in beauty and should be built into a single AM modulator class. But getting started is more important. I did not try this flow and I hope it is a correct implementation.
 
Hi Stefanodi - What you have produces double sideband suppressed carrier. You need to add in a constant carrier level, like 1.0. This is like vout = [1 + m(t)]*sin2*pi*15000*t wher m(t) is the modulating waveform that you have as a sine wave. Something like

Code:
AudioSynthWaveformDc     dc1;            //xy=71,634
AudioInputI2S            i2s2;           //xy=76,754
AudioSynthWaveform       waveform1;      //xy=78,688
AudioFilterFIR           fir1;           //xy=219,754
AudioMixer4              mixer1;         //xy=220,670
AudioEffectMultiply      multiply1;      //xy=359,699
AudioOutputI2S           i2s1;           //xy=449,759
AudioConnection          patchCord1(dc1, 0, mixer1, 0);
AudioConnection          patchCord2(i2s2, 0, fir1, 0);
AudioConnection          patchCord3(waveform1, 0, mixer1, 1);
AudioConnection          patchCord4(fir1, 0, multiply1, 1);
AudioConnection          patchCord5(mixer1, 0, multiply1, 0);
AudioConnection          patchCord6(multiply1, 0, i2s1, 0);
AudioControlSGTL5000     sgtl5000_1;     //xy=230,821

The mixer does the addition of the 1 and the modulating waveform coming from the ADC. This is lacking in beauty and should be built into a single AM modulator class. But getting started is more important. I did not try this flow and I hope it is a correct implementation.

Thanks again Bob for your availability.
I tried the code and the Amplitude Modulation and it works. :)
However, it functions as a controlled carrier.
Only in the presence of an incoming audio signal, it generates radio frequency.
Maybe my values ​​of the audio shield setup are wrong?
Here is the test code with my I-Q Mixer:

Code:
/* File: AM_TEST_TX-2.ino
 * Project's name: "Old Style A.M. RTX"
 * Initial project request by Stefano Homebrew
 * @ https://forum.pjrc.com/forum.php
 * Forum code by BOB LARKIN
 * Developed for Teensy 4.0 https://www.pjrc.com/teensy/
 * Audio shield PCM1808 & PCM5102A
 */
//****
#include <Wire.h>                         
#include <si5351.h> 
#include <ILI9341_t3.h>
#include <Audio.h>  
#include <SPI.h>
#include <SerialFlash.h>
//****Display ILI9341
#define TFT_DC    9
#define TFT_CS    10
#define TFT_RST   255  
#define TFT_MOSI  11
#define TFT_SCLK  13
#define TFT_MISO  12

#define BLACK    0x0000//My alternative colors
#define MARRONE  0x5041//My alternative colors

#define IF_FREQ 15000//****sine gen.

//****var pll frequency init.
volatile long freq = 27125000;//Tested in free 11Meters band
volatile uint32_t vfoFreq = 0;//for next implementations

//****var print frequency
byte unita,decine,centinaia,migliaia,decinemigliaia,centmigliaia,milioni ;  //variabili stampa freq.

//****************
AudioInputI2S            i2s2;           
AudioSynthWaveformDc     dc1;            
AudioSynthWaveform       waveform1;     

AudioFilterBiquad        biquad1;        //FILTER AUDIO INPUT

AudioMixer4              mixer1;      
AudioEffectMultiply      multiply1;   
AudioOutputI2S           i2s1;      

AudioConnection          patchCord1(i2s2, 0, biquad1, 0);
AudioConnection          patchCord2(dc1, 0, mixer1, 0);
AudioConnection          patchCord3(waveform1, 0, mixer1, 1);
AudioConnection          patchCord4(biquad1, 0, multiply1, 1);
AudioConnection          patchCord5(mixer1, 0, multiply1, 0);
AudioConnection          patchCord6(multiply1, 0, i2s1, 0);
AudioControlSGTL5000     sgtl5000_1;

//*******
ILI9341_t3 tft=ILI9341_t3(TFT_CS, TFT_DC, TFT_RST, TFT_MOSI, TFT_SCLK, TFT_MISO);
Si5351 si5351;  

//************************

void setup() {
  Serial.begin(9600);
  
//****init ILI9341  
  tft.begin();
  tft.setRotation(3);
  tft.fillScreen(ILI9341_BLACK);
  
//*****DISPLAY REM
  tft.setCursor(2, 232);
  tft.setTextColor(ILI9341_GREEN, ILI9341_BLACK);
  tft.setTextSize(1); 
  tft.print("AM_TEST_TX-2"); 
   
//*****PLL SET
  si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0, 0);
  si5351.set_correction(210000, SI5351_PLL_INPUT_XO);//My XTAL correction
  si5351.set_pll(SI5351_PLL_FIXED, SI5351_PLLA);
  si5351.drive_strength(SI5351_CLK2, SI5351_DRIVE_6MA);
  si5351.set_freq(freq * 400ULL, SI5351_CLK2);

//*****Audio shield SETUP
  AudioNoInterrupts();
  AudioMemory(16);
  sgtl5000_1.enable();
  sgtl5000_1.inputSelect(AUDIO_INPUT_LINEIN);   
  sgtl5000_1.volume(1.0);
  
  waveform1.begin(1.0,IF_FREQ,WAVEFORM_SINE);
  dc1.amplitude(1.0);
  
  mixer1.gain(0,1);
  mixer1.gain(1,1);

  biquad1.setLowpass(0,6000,0.7);
  biquad1.setHighpass(1,100,0.7);

  AudioInterrupts();
  
  delay(100);
    stampafrq();
}

//*****************LOOP
void loop() {

}

//***************PRINT @frequency DISPLAY
void stampafrq()
{       
   vfoFreq=freq; ///////////////////////////////////////////////////
    milioni = int(vfoFreq/1000000);
    centmigliaia = ((vfoFreq/100000)%10);
    decinemigliaia = ((vfoFreq/10000)%10);
    migliaia = ((vfoFreq/1000)%10);
    centinaia = ((vfoFreq/100)%10);
    decine = ((vfoFreq/10)%10);
    unita = ((vfoFreq/1)%10);
//******************************************
    tft.fillRect(112, 44, 169, 32, BLACK);
    tft.setTextSize(3);
    tft.setCursor(121,44);
    tft.setTextColor(ILI9341_WHITE);
    tft.print(milioni);//
    tft.print(".");
    tft.print(centmigliaia);
    tft.print(decinemigliaia);
    tft.print(migliaia);
    tft.print(".");
    tft.print(centinaia);
    tft.print(decine);             
}
 
I'm sorry, I added the carrier onto the 15 kHz and not the modulating voltage. Correcting that:#include <Audio.h>

Code:
AudioSynthWaveformDc     dc1;            //xy=80,871
AudioInputI2S            i2s2;           //xy=83,989
AudioSynthWaveformSine   sine1;          //xy=164,514
AudioFilterFIR           fir1;           //xy=224,981
AudioSynthWaveform       waveform1;      //xy=240,1027
AudioMixer4              mixer1;         //xy=242,901
AudioEffectMultiply      multiply1;      //xy=381,930
AudioOutputI2S           i2s1;           //xy=471,990
AudioConnection          patchCord1(dc1, 0, mixer1, 0);
AudioConnection          patchCord2(i2s2, 0, fir1, 0);
AudioConnection          patchCord3(fir1, 0, mixer1, 1);
AudioConnection          patchCord4(waveform1, 0, multiply1, 1);
AudioConnection          patchCord5(mixer1, 0, multiply1, 0);
AudioConnection          patchCord6(multiply1, 0, i2s1, 0);
AudioControlSGTL5000     sgtl5000_1;     //xy=250,1081

Also, the 1.0 for the DC is fine in float, but for this I16 you need to reduce that to 0.5 to allow room for the AM increase.

Is this closer?
 
stefanodi, just to make it visible for those without your hardware, I built an AM generator only and used a Queue to output the final waveform to the Serial Plotter. As it sits, it runs 50% modulation. Oh, and I also brought the input from a sine wave generator. Here is the INO:
Code:
// AM waveform fc = 11025 Hz, fm=441 Hz.
// Teensy audio
// Turn on Tools>Serial Plotter to see output
// Bob L

#include <Audio.h>

#define IF_FREQ 11025.0  //15000//****sine gen.

AudioSynthWaveformSine   sine1;
AudioSynthWaveformDc     dc1;            //xy=80,871
AudioInputI2S            i2s2;           //xy=83,989
AudioFilterBiquad        biquad1;           //xy=224,981
AudioSynthWaveform       waveform1;      //xy=240,1027
AudioMixer4              mixer1;         //xy=242,901
AudioEffectMultiply      multiply1;      //xy=381,930
AudioRecordQueue         queue1;
AudioOutputI2S           i2s1;           //xy=471,990
AudioConnection          patchCord1(dc1, 0, mixer1, 0);
AudioConnection          patchCord2(sine1, 0, biquad1, 0);
AudioConnection          patchCord3(biquad1, 0, mixer1, 1);
AudioConnection          patchCord4(waveform1, 0, multiply1, 1);
AudioConnection          patchCord5(mixer1, 0, multiply1, 0);
AudioConnection          patchCord6(multiply1, 0, queue1, 0);
AudioControlSGTL5000     sgtl5000_1;     //xy=250,1081

void setup() {
  Serial.begin(9600); delay(1000);
  AudioMemory(16);
  sgtl5000_1.enable();
  // The modulating waveform
  //sine1.amplitude(0.5f);     // 100% AM modulation
  sine1.amplitude(0.25f);     // 50% AM modulation
  sine1.frequency(441.0f);
  // The carrier, moved to fs/4 to minimize beats with the sample rate
  // Only a problem when looking at Serial Plotter, OK at 15 kHz over the air
  waveform1.begin(1.0f,IF_FREQ,WAVEFORM_SINE);
  dc1.amplitude(0.5f);    // Leave room for AM modulation
  mixer1.gain(0,1);
  mixer1.gain(1,1);
  biquad1.setLowpass(0,6000,0.7);  // Not needed for sine wve,
  biquad1.setHighpass(1,100,0.7);  //   but generally a good idea
  queue1.begin();
}

void loop() {
  static int count = 0;
  if(queue1.available() > 0)
     {
     count++;
     int16_t* pi = queue1.readBuffer();
     for (int ii=0; ii<128; ii++)
       if(count<10) Serial.println(*(pi+ii));
     queue1.freeBuffer();
     }
}

And also, my post #6 had an extra unconnected sine wave generator---not being use and harmless.

Have fun with all this!
 
stefanodi, just to make it visible for those without your hardware, I built an AM generator only and used a Queue to output the final waveform to the Serial Plotter. As it sits, it runs 50% modulation. Oh, and I also brought the input from a sine wave generator. Here is the INO:
Code:
// AM waveform fc = 11025 Hz, fm=441 Hz.
// Teensy audio
// Turn on Tools>Serial Plotter to see output
// Bob L

#include <Audio.h>

#define IF_FREQ 11025.0  //15000//****sine gen.

AudioSynthWaveformSine   sine1;
AudioSynthWaveformDc     dc1;            //xy=80,871
AudioInputI2S            i2s2;           //xy=83,989
AudioFilterBiquad        biquad1;           //xy=224,981
AudioSynthWaveform       waveform1;      //xy=240,1027
AudioMixer4              mixer1;         //xy=242,901
AudioEffectMultiply      multiply1;      //xy=381,930
AudioRecordQueue         queue1;
AudioOutputI2S           i2s1;           //xy=471,990
AudioConnection          patchCord1(dc1, 0, mixer1, 0);
AudioConnection          patchCord2(sine1, 0, biquad1, 0);
AudioConnection          patchCord3(biquad1, 0, mixer1, 1);
AudioConnection          patchCord4(waveform1, 0, multiply1, 1);
AudioConnection          patchCord5(mixer1, 0, multiply1, 0);
AudioConnection          patchCord6(multiply1, 0, queue1, 0);
AudioControlSGTL5000     sgtl5000_1;     //xy=250,1081

void setup() {
  Serial.begin(9600); delay(1000);
  AudioMemory(16);
  sgtl5000_1.enable();
  // The modulating waveform
  //sine1.amplitude(0.5f);     // 100% AM modulation
  sine1.amplitude(0.25f);     // 50% AM modulation
  sine1.frequency(441.0f);
  // The carrier, moved to fs/4 to minimize beats with the sample rate
  // Only a problem when looking at Serial Plotter, OK at 15 kHz over the air
  waveform1.begin(1.0f,IF_FREQ,WAVEFORM_SINE);
  dc1.amplitude(0.5f);    // Leave room for AM modulation
  mixer1.gain(0,1);
  mixer1.gain(1,1);
  biquad1.setLowpass(0,6000,0.7);  // Not needed for sine wve,
  biquad1.setHighpass(1,100,0.7);  //   but generally a good idea
  queue1.begin();
}

void loop() {
  static int count = 0;
  if(queue1.available() > 0)
     {
     count++;
     int16_t* pi = queue1.readBuffer();
     for (int ii=0; ii<128; ii++)
       if(count<10) Serial.println(*(pi+ii));
     queue1.freeBuffer();
     }
}

And also, my post #6 had an extra unconnected sine wave generator---not being use and harmless.

Have fun with all this!

Great! Now it's really good! I think, some other RADIO NOSTALGIC :rolleyes: will be happy. Thanks again Bob!:eek:
Stefano.
 
Back
Top