Floating-Point Audio Library Extension

I added another class AudioLMSDenoiseNotch_F32 to the F32 library https://github.com/chipaudette/OpenAudio_ArduinoLibrary . The Denoise uses the LMS algorithm to find coherency (via a small delay line) and then subtracts out any thing that lacks it. This has made its way into many radio systems, but has some quirks in terms of the resulting sound. The Autonotch is the same algorithm but turns the problem around and removes any signal that is coherent. This can be adjusted to be quite effective. The starting spot for this is again the Design Tool, http://www.janbob.com/electron/OpenAudio_Design_Tool/index.html. The LMS algorithm includes "leakage" to reset things when the signals go away and also normalization to keep the functioning the same when signal levels change.
 
I'm happy to report that due to the efforts of Giuseppe Callipo, and several others upon which his work is based, we have added a Convolution Filter class to the Floating Point Teensy Library. I believe this is basically the same filter used with Teensy audio by Frank, DD4WH and Brian Miller but with some changes. The feature of Convolution Filtering is a speed improvement relative to conventional FIR filtering.

This class is restricted to 512 point FIR tap equivalent. The built-in filter designs use the Kaiser window resulting in generally useful frequency responses. These designs cover Low Pass, High Pass, Band Pass and Band Reject responses. Provision is made for external input of coefficients to allow any filter type.

Measured 128 sample in update() is 139 microseconds (T4.x). Comparison with a conventional FIR, the AudioFilterFIRGeneral_F32, showed that a 512 tap FIR gave essentially the same response but was somewhat slower at 225 microseconds per 128 update. Also, note that this form of the computation uses about 44 kB of data memory where the direct FIR uses about 10 kB.

More information is available from the include file in the library, from the F32 Audio Design Tool and the Example, TestConvolutionFilter.ino.
 
Just to clarify: this a simple FFT convolution with full latency, rather than a partitioned convolution (low latency)?

(In other words it cannot be used with minimal phase FIR filters to give low latency?)
 
Yes, it the simple FFT convolution. This came about from radio systems where the goal was not huge sizes, but rather to get speed improvement on sizes like 512 taps. For these, latency was a secondary issue.
 
The OpenAudio_ArduinoLibrary Teensy F32 library has used a hybrid I16/I32/F32 sine wave generator AudioSynthWaveformSine_F32. I just re-wrote that to be completely floating point, allowing frequencies like 1234.567 to be produced correctly. The lookup table is now 512 entries allowing close to floating point accuracy to be achieved in the interpolated output. The old integer calls should still work OK. This (hopefully) keeps old code running as it was.

Additionally, I sometimes use the sine (and sine-cosine) generator for testing where the maximum purity of the generator is desired. There is now a new function pureSpectrum((bool true) that places a two-stage biquad IIR band pass filter on the output. The center frequency of this filter tracks any change in frequency(). In general, the -110 dBc spurs (Mainly odd harmonics) are dropped out of sight, i.e., down around-150 dBc. This feature is generally not needed for ordinary applications and the default is to not use it. It is also turned off at very low frequencies, relative to the sample rate, to not have accuracy problems with the filters.

The sine-cosine generator has also had the pureSpectrum option added to it. The floating point design tool http://www.janbob.com/electron/OpenAudio_Design_Tool/index.html has been updated to for all of this.
 
For those interested, here is my best illustration of the pureSpectrum filtering. There are two inputs coming from a sine/cosine pair,AudioSynthSineCosine_F32, running at 1000 Hz. This is going to a 4096 point FFT with complex I-Q inputs. The FFT is running with a Blackman-harris window to give as much view off frequency as possible. The complex input FFT distinguishes between positive and negative frequecies and we plot this with zero in the middle.

The dark blue trace is with the regular sine (and cosine) generators and various responses are seen up to about -110 dB below the carrier. Normally this is at a level that no one would care about! In this case, the question was whether these responses were due to the Sine wave process of the FFT one. Turning on the pureSpectrum filtering suggests they were due to the Sine generator, as the magenta trace is down at the windowing level, with the spurs more-or-less gone. Here is the graph:
PureSpectrum1000Hz.gif
The INO that was used is
Code:
// TestFFT2048iqEM.ino  for Teensy 4.x
// ***  EXTERNAL MEMORY VERSION of 4095 FFT  ***
// Bob Larkin 9 March 2021

// Generate Sin and Cosine pair and input to IQ FFT.
// Serial Print outputs of all 4096 bins.
//
// Public Domain

#include "OpenAudio_ArduinoLibrary.h"
#include "AudioStream_F32.h"

// Memory for IQ FFT
float32_t  fftOutput[4096];  // Array to allow fftBuffer[] to be available for new in data
float32_t  window[2048];     // Half size window storage
float32_t  fftBuffer[8192];  // Used for FFT, 4096 real, 4096 imag, interleaved
float32_t  sumsq[4096];      // Required if power averaging is being done

int jj;

// GUItool: begin automatically generated code
AudioSynthSineCosine_F32   sine_cos1;       //xy=76,532
//                                                                         Optional
// (float32_t* _pOutput, float32_t* _pWindow, float32_t* _pFFT_buffer, float32_t* _pSumsq)
//AudioAnalyzeFFT4096_IQEM_F32 FFT4096iqEM1(fftOutput, window, fftBuffer);      //xy=243,532
AudioAnalyzeFFT4096_IQEM_F32 FFT4096iqEM1(fftOutput, window, fftBuffer, sumsq);  // w/ power ave
AudioOutputI2S_F32         audioOutI2S1;    //xy=246,591
AudioConnection_F32        patchCord1(sine_cos1, 0, FFT4096iqEM1, 0);
AudioConnection_F32        patchCord2(sine_cos1, 1, FFT4096iqEM1, 1);
// GUItool: end automatically generated code

void setup(void) {

  Serial.begin(9600);
  delay(1000);

  // The 4096 complex FFT needs 32 F32 memory for real and 32 for imag.
  // Set memory to more than 64, depending on other useage.
  AudioMemory_F32(100);
  Serial.println("FFT4096IQem Test");

  sine_cos1.amplitude(1.0f); // Initialize Waveform Generator

  // Engage the identical BP Filters on sine/cosine outputs (true).
  sine_cos1.pureSpectrum(true);

  // Pick T4.x bin center
  // sine_cos1.frequency(689.0625f);

  // or pick any old frequency
  sine_cos1.frequency(1000.0f);

  // elect the output format, FFT_RMS, FFT_POWER, or FFT_DBFS
  FFT4096iqEM1.setOutputType(FFT_DBFS);

  // Select the wndow function, designed by FFT object
  //FFT4096iqEM1.windowFunction(AudioWindowNone);
  //FFT4096iqEM1.windowFunction(AudioWindowHanning4096);
  //FFT4096iqEM1.windowFunction(AudioWindowKaiser4096, 55.0f);
  FFT4096iqEM1.windowFunction(AudioWindowBlackmanHarris4096);

  // Uncomment to Serial print window function
  // for (int i=0; i<2048; i++) Serial.println(*(window+i), 7);

  // xAxis, See leadin discussion at analyze_fft4096_iqem_F32.h
  FFT4096iqEM1.setXAxis(0X03);    // 0X03 default

  // In order to average powers, a buffer for sumsq[4096] must be
  // globally declared and that pointer, sumsq, set as the last
  // parameter in the object creation.  Then the following will
  // cause averaging of 4 powers:
  FFT4096iqEM1.setNAverage(4);

  jj = 0;   // This is to delay data gathering to get steady state
  }

void loop(void)  {
  static bool doPrint=true;
  float *pPwr;

  // Print output, once
  if( FFT4096iqEM1.available() && jj++>2 && doPrint )  {
	  if(jj++ < 3)return;
      for(int i=0; i<4096; i++)
        {
//      Serial.print((int)((float32_t)i * 44100.0/4096.0)); // Print freq
        Serial.print(i);     // FFT Output index (0, 4095)
        Serial.print(" ");
        Serial.println(*(fftOutput + i), 8 );
	    }
      doPrint = false;
      }
  if(doPrint)
      {
      Serial.print(" Audio MEM Float32 Peak: ");
      Serial.println(AudioMemoryUsageMax_F32());
      }
  delay(100);
  }
The magenta response at -1000 Hz is interesting. It is from before the FFT, but only present when the filters are in place. The coefficients for the sine and cos filters are identical. The response is still due to differences in the 90 degree relationship (at the 10^-6 sort of level). Fun!
 
The OpenAudio_ArduinoLibrary now has AudioAlignLR_F32 objects to measure and correct Left-Right Codec startup time shifts. This is the old problem that Frank DD4WH nick-named "Twin Peaks" when used with I-Q radio systems. This implementation is T4.x compatible and is implemented as a library object.

The basic operation is to introduce a common analog signal to both ADC channels and measure the cross-correlation between the resulting digital outputs with varying delays. The maximum shift seen from the Codec is a single time sample so there are only three possible cases to correct, delay L, no shift or delay R.

The only challenge in all this is to introduce a common analog input to the two ADC channels. There are currently two methods implemented. One uses a Teensy digital I/O pin connected to the ADC line inputs with 100K resistors. I believe this is the preferred system because of its simplicity. It does, however, require a few lines of code in the INO to generate a fs/4 frequency square wave. The alternate method is to borrow the Codec DAC at startup to generate a fs/4 square wave. This needs audio switches to isolate the ins and outs after startup.

I have posted the library elements as "beta test," as I hope to remove the code for the second method. If anyone has thoughts on that, or can try the first I/O pin method out, I would appreciate hearing from you.

To help with using this element, there is an example program and it is in the F32 Design Tool under Inputs.
 
I revised the floating point AudioAlignLR_F32, making it easier to use (see previous post). Thanks to Mike, K7MDL for the help with this.

The new method normalizes everything to the sum of the absolute values of the four cross-correlators. This seems to work very well and eliminates the need for a variable threshold input from the INO. The normalizing factor xNorm is now part of the TPinfo structure and is readable from the INO. xNorm needs to be above 0.0001 to not have noise problems. 0.001 to 0.01 would appear to be reasonable levels. Huge values like 10 or 100 work great for the align L-R process but have problems with coupling digital noise into the analog input stream. Because of this, setThreshold() is no longer used and temporarily will print an error message.

The example test INO, TestTwinPeaks.ino, has the startup routine moved to setup(). There are several changes so, if you have been looking at all his, I suggest getting all 3 files.

I still see no need of the alternate hardware using the Codec DAC. Unless something shows up, I plan to take it out with the next cleanup. Simpler is better. Let me know if you see a need for the Codec signal generation.

I got a question about the big picture. It works like this. If you have an application that is sensitive to one sample period time difference in the L and R outputs of the SGTL5000, and other, Codecs, you can insert the AudioAlignLR_F32 object right after the AudioInputI2S_F32 ADC input. You place a few lines of code into the INO setup() (see AudioAlignLR_F32.h and the example file) and from then on the Align object will insert the correct shift in L-R time positions. It needs no tending after setup().
 
The two-resistor mode (i.e., TP_SIGNAL_IO_PIN) of the AudioAlignLR_F32 object seem to be working fine, including with noise coming into the audio. But strong coherent signals at various frequencies are problems. So, I added a band-pass filters in fron of the cross-correlators. These are about 2 kHz bandwidth. They allow much more coherent signal, but I still was able to cause trouble with a big square wave at a few kHz.

The new files are at gitHub as AudioAlignLR_F32.h and .cpp. They have a new function for testing called "void setLRfilter(bool _useLRfilter) ;" Called with true and the filters are in place (the default) and false takes them out. They are at fs/4 always. I hope to remove the filter enabling function. but it is handy for testing now.

Please report any test results using this method of automatically aligning the sample time for the L & R channels. Thanks.
 
highres I/O now possible

Hi from a new member (post #1),

I got a Teensy4 for cheap and compact audio processing, with the hardware potential to keep full highres. Then I found out about the "standard" library, its limitation to 44.1kHz, later this nice extension. However, the inputs and outputs are not up to par, I2S with float versions but still 16 bit, no float versions for S/PDIF.

So I "fixed" that, see my fork: https://github.com/IDC-Dragon/OpenAudio_ArduinoLibrary
Probably it won't stay, if chipaudette accepts my pull request.
Just to let you know, for now.

The I/Os have a configurable sample rate. Since the rest of the library can't know about it, you'd have to "normalize" frequencies of filters, generators, etc. by the ratio of the processing sample rate versus nominal AUDIO_SAMPLE_RATE_EXACT.
You have to be aware of who's the "clock master". Slaving to something external without an ASRC only makes sense when there is no hardware output pacing downstream. Perhaps I'll do an I2S slave with resampler later, like the just added AsyncAudioInputSPDIF3_F32.

I'm left with some confusion about which would be the "offical", latest, maintained audio library. There is also this repo: https://github.com/Tympan/Tympan_Library To me it looks like a special fork for that hearing aid.
 
#[IDC]Dragon that looks like a major contribution. We need to get this into the floating point F32 Teensy library.

It looks like there are really two areas. First is handling 24 or 23 bit I2S words on input and output. The second part uses this to have a S/PDIF interface. Both seem valuable and useful. My plan is to test the I/O, first to see if I can find any 16 bit issues and then to play with 24 bit I2S words. I will try to get to that ASAP.

I like the notion of optical connections for the audio and other uses as well. I need to get some hardware together for S/PDIF. An all optical interface for radios could also work very well, but that is a separate topic.

Do you have test examples for any of this? Feel free to post the test INO's here with the CODE tags. Or, GitHub works fine.

On the floating point (F32) Teensy audio library, formally, OpenAudio_ArduinoLibrary. This has never been integrated into the PJRC Integer Teensy Audio library. But, they work together and can be mixed without problems. The F32 library belongs to Chip Audette, who also has the Tympan hardware and library (using the Teensy). Chip stays busy with Tympan and I try to coordinate activities in the F32 library. The origins of this F32 library can be seen at the beginning of this thread. Chip, feel free to add more.

Thanks much for the contribution and this will be a good addition. Bob
 
This does look like a major contribution. Thank you [IDC]Dragon!

Will this perform 24 or 32 bit conversion at 96khz? Because if it does this is very exciting for pro audio users. My personal interest is 24bit/44.1k.

I presume that the USB inputs and outputs will need some additional work to operate at this new bit rate?
 
@Bob, I have made myself primitive test examples (basically pass-thru or a sincos generator to an output). Based on your "PassthroughF32", which now automagically does hires. These replaced lines print the level in dB, which I found helpful for my low-signal tests:
Code:
    if(peakL.available())  Serial.print(20*log10(peakL.read()), 2);
    Serial.print(" dB <-L   R-> ");
    if(peakR.available())  Serial.print(20*log10(peakR.read()), 2);
    Serial.println(" dB");
For S/PDIF, I copied the "PassThroughAsyncSpdif.ino" example from the standard library, used my _F32 classes instead, added the AudioSettings_F32 struct like in your code.

I don't expect any compatibility issues with 16 bit, since audio data is wisely left-aligned. I2S and S/PDIF got pretty well-tested by me, I do have quite some equipment. (Like, a DSP board to feed, a logic analyzer with I2S decoder, dumping into a .wav file)

The audio library I/O is somewhat sensitive to either foreground activity or other hardware interrupts. (It wasn't me, has been this way before.) The periodic Serial.print()s in the examples give audible clicks. I had to comment them out to get pristine audio. Adding some temporary debug statistics may be helpful to find out. In general, I'm amazed about the simplicity of the audio library. (I would probably have over-engineered it with buffer queues etc.)

Since my pull request, it appeared to me that I could have added some meta data of the new classes for the GUI tool.

@Jay, yes, you could do 96 kHz and 24 bit S/PDIF or 32 bit I2S, been there, have done that.
USB is indeed a different beast. It is not handled by the float library extension, USB_Audio_F32 only contains implicit conversions from/to 16 bit by patching in AudioConvert_I16toF32/F32toI16 objects. Data transfer is done by the original library. There is a pull request from Frank B to at least have the USB descriptors built from the static definition of AUDIO_SAMPLE_RATE_EXACT. So when you change that, the USB "soundcard" gets announced accordingly. But it won't give you more than 16 bits. I'm not an USB audio expert, what transfers are possible. It would probably damage the original library to change there.
 
I'm learning all about "automagically"!!!

After trying 16-bit stuff with no issues, I went to the SGTL5000 driver in the Teensy Audio Library and changed the I2S control to
Code:
write(CHIP_I2S_CTRL, 0x0010); // SCLK=64*Fs, 24bit, I2S format
I brought up the pass through INO. Wow, it worked great!!! Is that it? I think I am sending in 24-bit I2S, but of course the ADC in the SGTL is not close to clean enough to hear any difference.

This is great.
 
The full group of changes and additions from @[IDC]Dragon! are now in the F32 OpenAudio_ArduinoLibrary. Many thanks!!
 
Be aware that there is a problem with the f32 vs F32 designator on spdif files. I will get this fixed, but one may need a new pull from GitHub to get clean compile.

Also, I am adding the new files to the F32 Design Tool today.
 
I fixed the f32/F32 confusion and it compiles, so the library is good. When I tried to use input_spdif3 it compiled but I got a link error when it tried to tie the sample rate to AudioStream instead of AudioStream_F32. I don't see where this is coming from. I need to leave for a few days and will revisit this next week.

Also I added the S/PDIF classes to the Design Tool gui. More later.
 
Another new class, AudioSpectralDenoise_F32 has been added to the Floating Point Audio Library. Thanks to Graham Whaley for creating this class from the work of Frank, DD4WH, and others. This is a noise reduction technique based on analyzing the FFT and estimating the amount of noise, as opposed to speech, is in each spectral bin. Graham updated the Floating Point Design Tool (Filter/Spectral). He also made an experimenters toolbox of functions for those wanting to "tune up" the algorithm. See the functions in the Design Tool for information on this.

Background and explanation: https://github.com/df8oe/UHSDR/wiki/Noise-reduction
Floating Point Audio Library: https://github.com/chipaudette/OpenAudio_ArduinoLibrary
Floating Point Audio Design Tool: http://www.janbob.com/electron/OpenAudio_Design_Tool/

Thanks to Graham, Frank and the others that have developed this interesting method.
 
New additions to the F32 Audio library support Binary Frequency Shift Keying (BFSK) which is used in many radio systems, going way back. The classes are radioBFSKmodulator_F32, RadioFMDiscriminator_F32 and UART_F32. The Github site and the F32 Design tool links are in the previous #69 post. There are also three examples on Github, BFSK, BFSK_random and BFSK_snr.. To give some feel to this, here is the first example:
Code:
/*
 * BFSK.ino  Test the BFSK  at 1200 baud with repeated data.
 * The Serial Monitor should print:

OpenAudio_ArduinoLibrary  - Test BFSK
Resulting audio samples per data bit = 40
0The quick brown fox jumped...
1The quick brown fox jumped...
2The quick brown fox jumped...
3The quick brown fox jumped...
4The quick brown fox jumped...
Data send and receive complete

 *
 * F32 library
 * Bob Larkin 5 June 2022
 * Public Domain
 */

#include "OpenAudio_ArduinoLibrary.h"
#include "AudioStream_F32.h"
#include <Audio.h>

float* pDat = NULL;
float32_t fa, fb, delf, dAve;  // For sweep
struct uartData* pData;
float32_t inFIRCoef[200];
float32_t inFIRadb[100];
float32_t inFIRData[528];
float32_t inFIRrdb[500];

// LPF FIR for 1200 baud
static float32_t LPF_FIR_Sinc[40] = {
0.025f, 0.025f, 0.025f, 0.025f, 0.025f, 0.025f, 0.025f, 0.025f,
0.025f, 0.025f, 0.025f, 0.025f, 0.025f, 0.025f, 0.025f, 0.025f,
0.025f, 0.025f, 0.025f, 0.025f, 0.025f, 0.025f, 0.025f, 0.025f,
0.025f, 0.025f, 0.025f, 0.025f, 0.025f, 0.025f, 0.025f, 0.025f,
0.025f, 0.025f, 0.025f, 0.025f, 0.025f, 0.025f, 0.025f, 0.025f};

// The next needs to be 128 + the size of the FIR coefficient array
float32_t FIRbuffer[128+40];

// T3.x supported sample rates: 2000, 8000, 11025, 16000, 22050, 24000, 32000, 44100, 44117, 48000,
//                             88200, 88235 (44117*2), 95680, 96000, 176400, 176470, 192000
// T4.x supports any sample rate the codec will handle.
const float sample_rate_Hz = 48000.0f ;  // 24000, 44117, or other frequencies listed above (untested)
const int   audio_block_samples = 128;   // Others untested
AudioSettings_F32 audio_settings(sample_rate_Hz, audio_block_samples);  // Not used

RadioBFSKModulator_F32    modulator1(audio_settings);
AudioSynthGaussian_F32    gwn1;
AudioMixer4_F32           mixer4_1;
AudioFilterFIRGeneral_F32 inputFIR;
RadioFMDiscriminator_F32  fmDet1(audio_settings);
UART_F32                  uart1(audio_settings);
AudioAnalyzeRMS_F32       rms1;
AudioOutputI2S_F32        audioOutI2S1(audio_settings);
AudioConnection_F32       patchCord1(modulator1, 0, mixer4_1,  0);
AudioConnection_F32       patchCord2(gwn1,       0, mixer4_1,  1);
AudioConnection_F32       patchCord4(mixer4_1,   0, inputFIR,  0);
AudioConnection_F32       patchCord5(inputFIR,   0, rms1,      0);
AudioConnection_F32       patchCord7(inputFIR,   0, fmDet1,    0);
AudioConnection_F32       patchcord8(fmDet1,     0, uart1,     0);
AudioControlSGTL5000      sgtl5000_1;

void setup() {
   uint32_t spdb;
   static float32_t snrDB = 15.0f;
   static uint16_t dm0;
   static uint32_t nn, ii;
   static char ch[32];
   static uint32_t t = 0UL;

   Serial.begin(300);   // Any value, it is not used
   delay(1000);
   Serial.println("OpenAudio_ArduinoLibrary  - Test BFSK");

   AudioMemory_F32(30, audio_settings);
   // Enable the audio shield, select input, and enable output
   sgtl5000_1.enable();                   //start the audio board
   spdb = modulator1.setBFSK(1200.0f, 10, 1200.0f,   2200.0f);
   modulator1.setLPF(NULL, NULL, 0);    // No LPF
   modulator1.amplitude(1.00f);
   Serial.print("Resulting audio samples per data bit = ");
   Serial.println(spdb);

   mixer4_1.gain(0, 1.0f);  // Modulator in
   mixer4_1.gain(1, 1.0f);  // Gaussian noise in

   // Design a bandpass filter to limit the input to the FM discriminator
   for(int jj=0;  jj<12;   jj++)   inFIRadb[jj] = -100.0f;
   for(int jj=3;  jj<=11;  jj++)   inFIRadb[jj] = 0.0f;
   for(int jj=12; jj<100;  jj++)   inFIRadb[jj] = -100.0f;
   inputFIR.FIRGeneralNew(inFIRadb, 200, inFIRCoef, 40.0f, inFIRData);

   fmDet1.filterOutFIR(LPF_FIR_Sinc, 40, FIRbuffer, 0.99f); // Precede initialize
   fmDet1.initializeFMDiscriminator(1100.0f, 2350.0f, 2.0f, 3.0f);
   uart1.setUART(40, 20, 8, PARITY_NONE, 1);

   // See BFSK_random.ino for details of S/N measurement
   //   Signal power = 470.831299, 5625
   //   Noise power = 471.335632, 5625
   //   S/N in dB for S set to 0.414476 and N set to 1.0f: -0.0046
   // S/N=7 dB marginal but S/N=14 dB is solid
   snrDB = 14.0f;
   modulator1.amplitude(pow(10.0, 0.05f*(snrDB-7.65f)));
   gwn1.amplitude(1.0f);

   // Send a little data, five of these:
   // index ii 0         10        20        30
   //          v         v         v         v
   strcpy(ch, "0The quick brown fox jumped...\n"); // 32 char including ending 0
   ii = 0;    nn = 0;
   modulator1.bufferClear();
   delay(40);

   // Get UART synced up
   if( modulator1.bufferHasSpace() )
      modulator1.sendData(0X200);   // 0X00
   if( modulator1.bufferHasSpace() )
      modulator1.sendData(0X200);

   while(nn < 5)
      {
      if( modulator1.bufferHasSpace() )
         {
         // Serial.print("Send"); Serial.println((char)ch[ii]);
         dm0 = (uint16_t)ch[ii++];
         if(ii>30)   // Sends all including \n, but not the string zero.
            {
            ii=0;
            nn++;
            ch[0]++;    // Left hand character, 0 to 4
            }
         modulator1.sendData(0X200 | (dm0 << 1));  // Format ASCII to 8N1 serial
         }
      if(uart1.getNDataBuffer() > 0L)
         {
         pData = uart1.readUartData();
         Serial.print((char)pData->data);
         }
      }

   // Receive takes longer than transmit, so wait for the rest.
   t = millis();
   while((millis() - t) < 2000UL)    // Wait few seconds...
      {
      if(uart1.getNDataBuffer() > 0L)
         {
         pData = uart1.readUartData();
         Serial.print((char)pData->data);
         }
      }
   Serial.println("Data send and receive complete");
   }

void loop() {
   }

Comments and corrections are welcomed. Have fun.
 
A further note on the BFSK data transmission, is that I used the BFSK_random.ino, referenced in the last #70 post, to generate byte error statistics. These are shown in this graph:
BFSFErrorProb.gifThis is for 1200 bit/sec 8N1 and the number of samples ranged from a few thousand at low S/N to a million for the five highest S/N points. This is the probability of a byte error and the more common bit-error probably should be 1/10 of these values, at least for the higher S/N.

I don't have any data to compare with, but the values and curve shapes seem reasonable. I have not attempted to optimize characteristics, like filter shapes, so I don't know if this might be improved. I don't plan to go any further with this. If some one wanted to "tune up" the parameters, it could be interesting.

If any of you data transmission experts can offer an evaluation of this that would also be interesting!
 
A new item for the F32 audio library is an FT8 transmit class. FT8 is a communications protocol that is very popular in amateur radio.

The input is a short text string that is of a standard format. The radioFT8Modulator_F32 object then sends out 81 tones at a 0.16 second per tone rate..

The information on the class is in two places, the radioFT8Modulator_F32.h file that is part of the library and the floating point audio design tool.

At the github site there is an example INO file called FT8Transmit. It generates several messages, does the needed timing adjustments and can serve as a test generator, as it includes Gaussian white noise at a calibrated level.

I have tested this with the WSJT-X decoder software without problems. If you are into this kind of thing, give it a try. Reports would be appreciated.

A companion demodulator/decoder is in the works. The transmit side is useful by itself.
 
Again on the FT8 Modulator, there is filtering of the variable that sets the 1-of-7 tone frequency shift. This is Gaussian in both frequency and time domains and greatly reduces the spectrum of the transmitted signal. This is important as only about 50 Hz is available for each transmission with in a band of roughly 2000 Hz. I found the following plot to be interesting in that it shows the precision with which the filtering slows the transitions in frequency.
.FT8ControlWaveform.gif
There is also control in amplitude at the ends of the transmission to also confine the spectrum. That is not shown.
 
Bob, that is really fantastic and a long awaited feature. I can imagine the demodulation is even a lot more demanding!

I have a lot of other obligations at the moment, so can't test this now, but can't wait until I get the time !
 
Hi Frank - It is great to catch you here. I saw that you did some work with the Goba/Hill software for the Teensy. I used that for creating the 81 tone sequence. That seems to be a real time saver and I hope the same procedure works for the receive side. If so, I really only need to decimate to a 6400 sample rate and adapt the existing library 2048 FFT to put the power outputs into the proper format. Time will tell. We'll stay in touch. Bob
 
Back
Top