Floating-Point Audio Library Extension

Great to hear!
You probably know that there already is a Teensy implementation of an FT8 decoder, have a look here, maybe there is something useful in it. They use 32ksps and decimate by 5 to get the bin spacing right, if I remember correctly.
https://github.com/DD4WH/Pocket_FT8
 
I would really appreciate some guidance regarding the AudioConvert object in the F32 library. I'm using that as the base for a new audio object, which I can detail later.

Something I've noticed on my scope is that, if I send the output of the AudioConvert_I16toF32 to an I2S output, I can see it is only sending 16 bits rather than 32 bits. Has anyone else found or tested this? I would have thought the output would be 32 bits, as I've measured the I2S output with input from AudioSynthWaveformSine_F32 and it shows 32 bits on the scope.

I have two sets of code to test this. Here's the one with all F32 objects, which shows 32 bits on the scope:
Code:
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>
#include "OpenAudio_ArduinoLibrary.h"

// GUItool: begin automatically generated code
AudioSynthWaveformSine_F32  sine2;          //xy=179,339
AudioSynthWaveformSine_F32  sine1;          //xy=184,267
AudioOutputI2S_F32          audio_out;   //xy=600,261
AudioConnection_F32         pc1(sine1, 0, audio_out, 0);
AudioConnection_F32         pc2(sine2, 0, audio_out, 1);
AudioControlSGTL5000        sgtl5000_1;     //xy=591,96
// GUItool: end automatically generated code

void setup(void) {

  AudioMemory(20);
  AudioMemory_F32(20);

  sgtl5000_1.enable();
  
  sine1.frequency(300);
  sine1.amplitude(0.5);
  sine1.begin();

  sine2.frequency(600);
  sine2.amplitude(0.8);
  sine2.begin();

} 

void loop() {}

Here's the code starting with sine waves from the original library, then converting to F32 and sending to an F32 I2S output (which only outputs 16 bits on the scope, rather than 32 bits):
Code:
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>
#include "AudioStream_F32.h"
#include "OpenAudio_ArduinoLibrary.h"

// GUItool: begin automatically generated code
AudioSynthWaveformSine      sine2;          //xy=179,339
AudioSynthWaveformSine      sine1;          //xy=184,267
AudioConvert_I16toF32       convert1;
AudioConvert_I16toF32       convert2;
AudioOutputI2S_F32          audio_out;   //xy=600,261
AudioConnection             pc1(sine1, 0, convert1, 0);
AudioConnection             pc2(sine2, 0, convert2, 0);
AudioConnection_F32         pc3(convert1, 0, audio_out, 0);
AudioConnection_F32         pc4(convert2, 0, audio_out, 1);
AudioControlSGTL5000        sgtl5000_1;     //xy=591,96
// GUItool: end automatically generated code

elapsedMillis elapsed_time_serial;

void setup(void) {

  // Set up serial monitor
  Serial.begin(9600);

  AudioMemory(2);
  AudioMemory_F32(4);

  sgtl5000_1.enable();
  sgtl5000_1.volume(0.6);
  
  sine1.frequency(300);
  sine1.amplitude(0.5);
  // sine1.begin();

  sine2.frequency(600);
  sine2.amplitude(0.8);
  // sine2.begin();

} 

void loop() {

  // Send memory usage to serial
  if (elapsed_time_serial > 1000) {
    elapsed_time_serial = 0;
    Serial.print(AudioMemoryUsageMax());
    Serial.print(",");
    Serial.println(AudioMemoryUsageMax_F32());
    AudioMemoryUsageMaxReset();
    AudioMemoryUsageMaxReset_F32();
  }
}

I would have expected to see 32 bits on the scope, so I'm confused as to why I'm only seeing 16 bits.

The next issue I'm having is trying to add a second input from the Int16 library. My understanding is the point of the AudioConvert object is so the 2 libraries work together, and certainly my example above suggests this is true (albeit for the 16bit output). However, if I modify the AudioConvert_I16toF32 class to read in a second input (using receiveReadOnly(1)), it stops working properly and I get intermittent output on the I2S bus. Here's the modification to the update function:

Code:
void update(void) {	
	//get the Int16 block
	audio_block_t *int_block_1;
	int_block_1 = AudioStream::receiveReadOnly(0); //int16 data block
	if (int_block_1==NULL) return;

	//allocate a float block
	audio_block_f32_t *float_block;
	float_block = AudioStream_F32::allocate_f32(); 
	if (float_block == NULL) {
	  AudioStream::release(int_block_1);
	  return;
	}

	// Get a second Int16 block (but don't actually do anything with it)
	audio_block_t *int_block_2;
	int_block_2 = AudioStream::receiveReadOnly(1); //int16 data block
	if (int_block_2==NULL) {
	AudioStream::release(int_block_1);
	AudioStream_F32::release(float_block);
	return;
	};

	//convert to float
	convertAudio_I16toF32(int_block_1, float_block, float_block->length);

	//transmit the audio and return it to the system
	AudioStream_F32::transmit(float_block, 0);
	AudioStream_F32::release(float_block);
	AudioStream::release(int_block_1);
	AudioStream::release(int_block_2);
};

I should add that I have increased the number of inputs for that function from 1 to 2 with
Code:
AudioStream_F32(2, inputQueueArray_f32)
and
Code:
audio_block_f32_t *inputQueueArray_f32[2];
One query I have is should the inputs this function not be from the I16 library rather than the F32 library?

Is there something obvious I'm missing? I'm not even trying to do anything with the second I16 block at the moment, just reading it in seems to cause issues. The result is gaps in the audio - the higlighted portion in the image below is 128 samples:
Screenshot 2022-09-28 103253.jpg

I've had issues with gaps before that have been resolved by allocating more memory, but that doesn't seem to help in this case.
 
The I16 to F32 only converts the 16 values to floating point. It does no interpolation of the data. So the resolution remains that of I16.

Also, the mantissa of F32 is 24 bits (not 32), so that limits the resolution of F32. The total range benefits from the exponent range, but that just scales the resolution limited range.

One more thought is that the F32 sine wave generators, such as AudioSynthWaveformSine_F32, will produce the full 24-bit resolution and accuracy. That can be used in an all F32 sketch. I'm not sure that is what you are looking for, though. That looks like:
Code:
AudioSynthWaveformSine_F32 sine2;          //xy=119,887
AudioSynthWaveformSine_F32 sine1;          //xy=120,831
AudioOutputI2S_F32       audioOutI2S1;   //xy=327,857
AudioConnection_F32          patchCord1(sine2, 0, audioOutI2S1, 1);
AudioConnection_F32          patchCord2(sine1, 0, audioOutI2S1, 0);
AudioControlSGTL5000     sgtl5000_1;     //xy=268,937

On the second question, why can't you just use two of the I16 to F32 blocks?

Bob
 
Thanks Bob. I see your point about only 16 bits getting converted to F32, and there is no interpolation. In my mind, I still imagine it being represented by 32 bits, but I'm happy to put that down to me not getting my head around it!

It would help if I explained my goal, and I do wonder whether other people may find useful. My goal is to transmit 4 channels of data over a single stereo I2S bus. My thought is, if I have four 16 bit channels coming in (I use the TDM input), i.e. 64 bits in total, why can't I use the two 32 bit bit I2S outputs (i.e. 64 bits total). My plan is this:

  • Receive four channels of 16 bit audio
  • Convert all 16 bit signed integers to unsigned (to allow for accurate bit shifting)
  • Deal with the 4 channels as two pairs
  • For each pair, bit shift one of the values 16 bits to the left, then combine with the other value with the OR operator, to produce a single 32 bit unsigned integer
  • If I do this for both pairs of channels, I will end up with two 32 bit channels (left and right), each containing two channels worth of data, giving a total of 4 channels
  • At the other end, it will need to be unpacked by splitting the 32 bit values back into separate 16 bit values (this isn't a problem at my receive end)

My experiments with the AudioConvert object were just testing the water with the library to see what was possible. I would need two I16 inputs to be able to combine into a single 32 bit output. Ideally, I would keep it as a 32 bit integer rather than float, as converting into and out of floats will introduce rounding errors. I'm happy to live with that for now as a proof of concept, although I think I've seen that the F32 library converts to integer before transmitting on the I2S bus anyway (I may be wrong).

I'm at the limit of my ability with C/C++ here, so any guidance would be greatly appreciated.
 
Last edited:
FT8 Reception - The FT8 transmitter was added into the floating point audio library a couple off months ago and is discussed in several posts above . The FT8 receive function has been added, as radioFT8Demodulator_F32. As usual, this is available as part of the OpenAudio_ArduinoLibrary and the F32 audio design tool. Post #72 above has some info on what FT8 communications protocol is about, for those not familiar with it.

Here is a sample from 14074 kHz with 15 stations being decoded:

Code:
FT8 Received TRansmissions, Noise Type=H
0 12:09:09  msg: AE9XT HK3J RR73  dt=0.48 freq=1196 snr=-11
1 12:09:09  msg: KG7PD KF0IPD -08  dt=0.16 freq=559 snr=-2
2 12:09:09  msg: TA8BB W4BOG R-09  dt=1.44 freq=450 snr=3
3 12:09:09  msg: CQ KM4SXE EL96  dt=0.00 freq=737 snr=2
4 12:09:09  msg: WS3M AG1T +06  dt=1.44 freq=943 snr=0
5 12:09:09  msg: CQ KC3UII FM16  dt=0.64 freq=2100 snr=-12
6 12:09:09  msg: CQ K0JV DN84  dt=0.48 freq=315 snr=-6
7 12:09:09  msg: F4EAD KK4RR EM95  dt=0.48 freq=1331 snr=-12
8 12:09:09  msg: 5V7RU AC0DH/P R-04  dt=1.44 freq=1600 snr=-7
9 12:09:09  msg: CQ N7MDW EM75  dt=0.48 freq=1550 snr=-11
10 12:09:09  msg: 5V7RU K6QGV DM13  dt=0.48 freq=1896 snr=-7
11 12:09:09  msg: LA1HU W4ABF 73  dt=0.64 freq=1815 snr=-7
12 12:09:09  msg: 5V7RU N4QWZ R-12  dt=0.64 freq=615 snr=-2
13 12:09:09  msg: RU3DF VE4YH R-14  dt=0.80 freq=1690 snr=0
14 12:09:09  msg: 5V7RU AA4QM R-03  dt=0.48 freq=1040 snr=1

The sensitivity seems to be within 2 dB of the WSJT-X code at -18 dB snr or so (in the strange 2500 Hz BW of noise) Decode time for 40 candidates is just over 0.1 sec, which is very acceptable.

Frank (DD4WH) I did start with the Charlie Hill Pocket-FT8 code. I ended up using the audio library class to build arrays of 2048 floats, ready for the INO to do the FFT's (194 of them in 15 seconds). The use of the output of the FFT's has been changed, and I added noise measuring routines to allow calculation of snr. In a busy over the air tests, the noise measure still makes some errors, but I think it is a good approach.

The example INO, FT8Receive is a receive demo only. I have tried to minimize complexity by only receiving signals and printing a single line to the Serial output. I believe that this can all fit into the full transmit/receive waterfall app like Charlie had put together. I have no plans to do that here. All of the heavy lifting for coding and decoding comes from the wonderful code of Karlis Goba: https://github.com/kgoba/ft8_lib. The example FT8Receive does take a limited collection of commands over the Serial link to do things like synchronizing the clock. The example ino is at https://github.com/chipaudette/OpenAudio_ArduinoLibrary/tree/master/examples/FT8Receive

The only supported sample rates are 48 and 96 ksps. The 48 is interpolated to 96 and then 96 is decimated to 6.4 ksps in two stages, /5 and /3. FIR filtering is used in both decimation stages to be sure stray signals are kept out.

That is it in a nutshell. Bob
 
As part of the example INO's for the FT8 transmit class listed above is a test generator that sends out additive white Gaussian noise plus 7 FT8 FSK signals. These are of accurate signal-to-noise ratio and suitable for evaluating various decoders for the sparse Hamming space modulation of FT8. A full write up, intended for people not familiar with the Teensys has been posted.

The simplification that comes from the use of the audio object library system is big. It can be seen from the Design Tool block diagram.
FT8GenDesign.gif.
 
Bob, that looks excellent! Especially the idea of having such a test generator is superb and provides one with the necessary tool to test the FT8 decoder!

The sensitivity figure you give is fantastic, unbelievable it is so near to the WSJT-X benchmark!

I am heavily involved in other stuff now, so I cannot test at the moment, but this seems a quite nice X-mas break distraction ;-). It seems I broke my Teensyduino installation, so there is some basic work to do before I can test . . .

Having this built into the Teensy Convolution SDR or into the SDT T41 seems a good idea, I think :).

Keep up the good work!

Frank DD4WH
 
Hi Frank - Thanks for the comments and thoughts. You are the wizard of this stuff and your ideas are most helpful. Besides, it lets you take a break from bat listening (I want to pursue that one sometime!)

On the FT8, I think the transmit and receive functions are in good shape. Many thanks to Karlis Goba for making this possible. To combine all this into QSO's needs something like what Charley Hill (W5BAA) did. I don't think that is part of the F32 library and depends a lot on the user interface. So I do not plan to do anything there. But, for your receiver, I guess most of the FT8Receive example will drop in. Have fun! Bob W7PUA
 
AudioPlaySDWav_F32 Added - This reads a WAV file from an uSD card into the Teensy F32 audio stream. This has been in the Teensy I16 Audio library and is now available with floating point audio conversion. The basic use and functions are the same for both I16 and F32. But, in addition, the floating point version allows use of other sample rates. If the WAV file and the Audio sample rate are, say, 48 ksps, nothing special is needed other that starting the INO program with (see the WavFilePlayer example)
Code:
const float sample_rate_Hz = 48000.0f;
AudioSettings_F32 audio_settings(sample_rate_Hz, 128);
But, there is also provision for running the WAV file at an integer sub-multiple of the Audio Sample Rate. For instance, the Audio Sample Rate could be 48 ksps and the WAV file recorded at 12 ksps. This is useful in cutting the size of the WAV files for voice applications.

The sub-multiple rates use interpolation of the data from the WAV file, and include an INO supplied FIR filter to finish the interpolation.

There is no ability to have other rational rate changes, like 441/480. Is there a need for such?

Included are three examples of the WAV file usage included: SdWavPlayer, WavFilePLayer and WavFilePlayer2.

Code for this class and examples are at Github. A lot of detail is on the right pane of the Open Audio F32 Design Tool.
 
Relative to the AudioPlaySDWav_F32, the sub-multiples for the WAV file sample rate are currently 1, 2, 4, or 8. Restricting this to not include, say 3 and 5, is historic and I believe no longer needed.. I have it on my list to make it "any integer."
 
Added to the F32 Audio library is radioCESSBtransmit__F32. CESSB stands for Controlled Envelope Single Sideband, which is a process developed for ham radio by Dave Hershberger, W9GR. The include file for the class has the basic references to Dave's work Nutshell wise it allows the average power of a voice modulated SSB radio transmitter to be more than doubled without either an increase in peak power nor annoying sounds to the voice.

The F32 Audio library is at Github. An example INO uses a WAV file to test the CESSB.

The Floating Point Audio Design Tool includes the CESSB class along with design information.
 
The class for transmitting CESSB in #87 above does not work well with the simple I-Q Zero-IF type of radios. There is a weak tone due to the Weaver method of SSB generation, along with finite hardware mixer LO isolation. So, now there is a companion class, radioCESSB_Z_transmit_F32 that uses the phasing method of SSB generation. Otherwise, the operation is the same and both are good at doing the power increase that was desired. If you are doing frequency up conversion in DSP, the Weaver method works with no tone added.

There is an associated .INO example and, of course, both classes are in the Floating Point Design Tool referenced is #87.
 
The Controlled Envelope Single Sideband (CESSB), as implemented in the two classes above, is an improvement for a standard modulation used for amateur radio voice communications called Single Sideband. The CESSB addition involves some non-linear processing that is not obvious. For those interested in learning more, I put together some notes that include the original papers as references at the bottom and quite a bit of detail about the specific Teensy implementations. The latter includes measured performance. This is all located at http://www.janbob.com/electron/CESSB_Teensy/CESSB_Teensy.html
 
A note that the AudioPlayQueue_F32 class has been updated to have the same flexibility as in the Teensy Audio I16 library. Thanks much to Jonathan Oakley. This is reflected in the code and the OpenAudio F32 Design Tool.
 
Bob,
tried today to receive and decode FT8 with your FT8Receive function. I use my Teensy Convolution SDR and a magnetic loop indoor antenna for reception and production of FT8 audio, and I feed the audio to another Teensy 4.0 with a PCM1808 ADC running your example code.

Works perfectly! thanks a lot for this nice module! Now its time to merge the two Teensys into one and make the SDR decode FT8 by itself without needing another helper Teensy. The CPU load on the Teensy SDR is only 20% @192ksps sample rate (FCPU = 512MHz), so plenty of processing power left . . . :). I have to think about this, because its really not nice to pull out the code from the audio classes . . . would be nicer to rebuild the Teensy SDR with your classes . . . but also much more work, I think. Will make up my mind and see how far I can get. Also displaying the mass of decoded FT8 messages on such a small screen is a challenge.
 
Just thinking loud: if I integrate the FT8 class as-is into the Teensy Convolution SDR, it would run all the time even if it is not used. And if the user increases the sample rate (I regularly use 256ksps), it could easily be too fast for the T4. Hmm, will think &#55358;&#56596; more.
 
I'm following along here with much interest, as I would eventually like to be able to build a stand-alone Teensy + TFT touchscreen FT8 transceiver (learning more about the how the DSP approach to FT8 RX/TX works along the way). One of my fellow members of our local radio club has found a variety of (claimed) FT8 transceivers that make use of the Teensy for some of the functionality. Unfortunately, many of them depend upon external hardware (they typically are made to hook up to an existing hardware transceiver kit), and/or they include specialized hardware (single-chip receiver, etc.) doing a portion of the receive function. I would prefer as much as possible for everything to be handled strictly within the Teensy (maybe I'm hoping for too much ??).

Thanks to both of you: Bob & Frank for your efforts in this regard & for your kindness in making these capabilities available for use by others !!

Mark J Culross
KD5RXT
 
Hi Frank and Mark - Frank, it is great that you were able to test the FT8 module. My experiments showed it to be within about 2 dB of the WSJT-X sensitivity and also to be able to do at least 15 decodes at once. So it seems OK.

Frank, when you used the two Teensies you could run the FT8 at 48 or 96 ksps rate. They don't support anything else, I think they could work with dynamic sample rates, but I have not tried that. Then you could use 48 ksps for FT-8 and 256 for FM broadcast, or whatever. I'll be interested in following what you do.

Mark, glad to have you thinking about this stuff. There are a lot of options for hardware and software. Frank's Convolution SDR has evolved into a fancy DIY hardware package and a shiny clean version of his software (as an experimenter, I love the original, but each to their own!) There are others, as you have seen. K7MDL/Keith SDR are based on the Teensy F32 library. That approach is not the most efficient in terms of processor time, but with the T4.x it is sort of a non-issue. The library approach is easy to follow and to experiment with. But they all need some sort of hardware.

I am getting back to playing with superhet hardware! Baseband I-Q is simple and works very well for getting wider bandwidths, but zero frequency is always in some annoying place and the approach requires some sort of 5351A fine tuning, with associated spurs. Farhan's RPi sbitx uses the superhet approach as did the DSP-10 of 1999. To get started, though, everyone needs to play with baseband I-Q!

The F32 library of this thread has all the pieces for either baseband or superhet approaches.

Cheers, Bob W7PUA
 
Mark & Bob,

independently from our conversation, a few days ago I read about Farhans sbitx for the first time! Very exciting and also very interesting that the DSP-10 also follows a superhet approach. I read your DSP-10 papers many years ago (they are 24 years old now . . .) and had forgotten about that, will reread :). I would be interested to hear about performance: do you think the superhet approach with subsequent ADC and DSP in the Teensy would lead to better performance than a QSD frontend ? Hmm, will read more and think about that.

Mark, for a quick start, you could buy one of the available SDR QSD Rx kits, eg. from QRP labs and use the Teensy Convolution SDR software. But honestly, I would recommend using the block approach with the Teensy F32 lib. Bob and Chip have done such a marvellous job in putting together all kinds of stuff (including CESSB, which had been on my wishlist for a long time ;-). Also, there is the T41-EP TRX by Al Peter and Jack Purdum, the Receiver part of which is based on the Teensy Convolution SDR code.

Also, in the last days, I have been thinking about a an FT8-only Teensy Transceiver, maybe only for one band. Take a simple QSD frontend, a Si5351 with that small XO chip (high freq stability), add the Teensy and maybe a class E transmitter concept like in the QRP labs QCX (for a one band TRX) or a class D transmitter like in the QDX (for a multiband TRX). And the software could be based on the Teensy F32 lib and take parts of the UI from the Pocket_FT8 handheld by W5BAA (https://github.com/DD4WH/Pocket_FT8). Just an idea.

BTW, I now skipped the int16 audio lib completely in the Teensy Convolution SDR and now use the Teensy F32 audio lib exclusively for the queues. Works nicely and uses less memory! The SDR and the FT8 decoding are now running on the same Teensy 4.1.
Thanks a lot for making this available with the audio lib, Bob!

Now my next step is to display the FT8 messages on the TFT rather than in the Serial monitor.

Example on 17M here in Central Europe, 16:00 UTC

Code:
ft8_decode Time, uSec = 105249
= = = = = SYNC TIME 15 = = = = =
FT8 Received TRansmissions, Noise Type=H
0 16:48:59  msg: CQ CT1EEX IM58  dt=0.16 freq=1178 snr=176 km=2240 az=238.4
1 16:48:59  msg: 7Q7CT EB3JT JN01  dt=0.16 freq=925 snr=181 km=1439 az=224.1
2 16:48:59  msg: JI1RZI F8DBF -14  dt=0.80 freq=2037 snr=178
3 16:48:59  msg: CQ D1DX KN87  dt=0.32 freq=1528 snr=180 km=1781 az=95.0
4 16:48:59  msg: CQ UD8S LO71  dt=0.32 freq=456 snr=181 km=2868 az=73.3
5 16:48:59  msg: CQ UR5ZD KN58  dt=0.16 freq=1371 snr=183 km=1326 az=97.5
6 16:48:59  msg: 9M2MRS OH5XX KP30  dt=0.32 freq=2246 snr=181 km=1321 az=35.4
7 16:48:59  msg: IW4APB RJ8C RR73  dt=0.32 freq=1818 snr=179
8 16:48:59  msg: IK8HIS VA2QR -04  dt=0.00 freq=1731 snr=179
9 16:48:59  msg: 7Q7CT UR4QGZ KN77  dt=0.16 freq=637 snr=179 km=1643 az=97.0
10 16:48:59  msg: 7Q7CT RC2Y KO73  dt=0.16 freq=1425 snr=180 km=1500 az=72.9
11 16:48:59  msg: 9M2MRS US1EA 73  dt=0.32 freq=1653 snr=179
12 16:48:59  msg: VU2WJ EA3EDU R-08  dt=0.00 freq=1062 snr=178

Not sure what happened to the SNR values ? Bob, do you have an idea ?

EDIT:

Also, I have to find a way to switch off the FT8 decoding during all menu actions and when switching to WFM Rx with 256ksps. Seems the T4 is a little overloaded with that . . .

CPU Load (T41. @512MHz) is quite low, 6.3% max, memory use goes up to a max of 109 sometimes, but is 42 or 89 on average:
(my first decode with Teensy at more than 10,000km distance --> Indonesia on 12M . . .)
Code:
Proc = 2.96 (6.32),  Mem = 0 (0)
Proc = 2.96 (2.96),  MemF32 = 42 (44)
Proc = 2.96 (6.32),  Mem = 0 (0)
Proc = 2.96 (2.96),  MemF32 = 42 (44)
FT8 Received TRansmissions, Noise Type=H
0 18:27:59  msg: CQ YB1JQC OI33  dt=0.64 freq=881 snr=180 km=10851 az=91.0
1 18:27:59  msg: HS7WMU EA7DJN IM88  dt=0.64 freq=1484 snr=179 km=1908 az=227.0
2 18:27:59  msg: CQ EA1EW IN70  dt=0.64 freq=1037 snr=178 km=1844 az=235.5
3 18:27:59  msg: DD1QL EA7NC IM66  dt=0.64 freq=1381 snr=181 km=2297 az=231.2
 
ft8_decode Time, uSec = 115423
= = = = = SYNC TIME 15 = = = = =
 
Last edited:
after a restart of the Teensy, it seems the SNR readings are OK!

Code:
= = = = = SYNC TIME 15 = = = = =
FT8 Received TRansmissions, Noise Type=L
0 18:44:26  msg: CQ VU3FGJ MK65  dt=2.72 freq=1271 snr=-21 km=6606 az=104.2
1 18:44:26  msg: DM2DXA EB7DLN 73  dt=2.72 freq=2040 snr=-21
 
ft8_decode Time, uSec = 124573
= = = = = SYNC TIME 15 = = = = =
Block error
FT8 Received TRansmissions, Noise Type=L
0 18:44:41  msg: DB1NAC EA4HTI IM68  dt=2.72 freq=1709 snr=-4 km=2124 az=234.9
1 18:44:41  msg: VU3FGJ DK4PL -03  dt=2.72 freq=906 snr=-2
2 18:44:41  msg: K3SAE EA7PY IM66  dt=2.72 freq=1034 snr=0 km=2297 az=231.2

But then I change band and the SNR values seem screwed again:

Code:
0 18:49:28  msg: CQ YO2BWC KN15  dt=0.80 freq=1468 snr=181 km=993 az=128.3
1 18:49:28  msg: CQ R110AP   dt=1.60 freq=653 snr=179
2 18:49:28  msg: CQ LZ2KTS KN22  dt=0.80 freq=1571 snr=180 km=1349 az=133.2
3 18:49:28  msg: 9A5E DM3XIF R+11  dt=0.80 freq=950 snr=180
4 18:49:28  msg: DK0MRT SP5ALV 73  dt=0.80 freq=787 snr=183
5 18:49:28  msg: CQ IK3/UY7LA   dt=1.12 freq=1918 snr=179
6 18:49:28  msg: IK2LEY F1LPT +02  dt=0.96 freq=1815 snr=180
7 18:49:28  msg: G6XOU R6LNH R-16  dt=2.24 freq=1690 snr=180

Do you have an idea what could be wrong ?

EDIT:

When I change sample rates in the menu (I use the routine by FrankB manipulating the I2S clocks), do I have to set something else for the F32 library ?
 
Frank, I would like to chat about the architecture issues, like superhets and things. But, I'm almost out of time for today. The same for a pocket FT-8 in the Charlie Hill W5BAA spirit. That would be a fun item

For now, though, the S/N calculation can be very wrong on busy bands. I do not know what WSJT-X does when there is no clear space to measure noise. I added the SNR as before it was not being made at all. Or, maybe there is a bug there somewhere.

In the F32 library, I am trying to watch the sampling rate controls to allow dynamic control. I may have missed some, but if I find them, I will fix them. Some classes, like an audio mixer/adder is not even dependent on sample rate. Ones, like sine wave generators just need to have the phase increment scaled. But, ones like the FT8 are coupled to the sample rate in multiple ways. Internally FT8 needs to run the FFT at 6400 sps. To get this from common ADC rates generally requires a full interpolation/decimation rate change. Making that general and able to work with any sample rate is beyond me!! So, I implemented 48 ksps and 96. If somebody figures out other rates, I can add them in or one could go the pull request route. BTW 44.1 and 6.4 are not an easy pair to work with.

But, the general way to specify sample rate is to add the code

Code:
const float32_t sample_rate_Hz = 48000.0f;
const int       audio_block_samples = 128;
AudioSettings_F32 audio_settings(sample_rate_Hz, audio_block_samples);

AudioInputI2S_F32        audioInI2S1(audio_settings); 
RadioFT8Demodulator_F32  demod1(audio_settings);

// and down in setup
AudioMemory_F32(50, audio_settings);
This will setup the initial sample rates at 48000. For dynamic data rate changes, the FT8 receive class has a function setSampleRate_Hz(const float32_t &fs_Hz) that can select either 48000 or 96000. I have never put together an example of dynamic rate change for FT8, but I believe it is OK.

More later Bob
 
A couple of thoughts on superhet architecture. I'll keep this brief, or it would need a new thread.

An assumption is that fine frequency RF hardware synthesizers have coherent spurious outputs at unacceptable levels (like -60 to -90 dBC). The DDS function implemented in software has a small enough resolution to make the spurious levels much, much lower. Implemented in hardware, the divide by N synthesizer has only a pair of spurs and these are always spaced off by the reference frequency.

To me this favors doing all hardware frequency changing using the divide-by-N. This, of course, is too coarse for tuning and requires a follow-on DSP-base tuning. Thus the superhet.

It is possible to do the first conversion as I-Q, using say 5 kHz steps, right into the DSP frequency range, say 15 kHz. This works fine for receive, but is almost impossible for transmitting, due to both lack of mixer LO rejection and intermodulation in the transmit amplifiers. That leads to a hybrid system with a DSP first I-F for receive, but a 5351 type of zero-IF for transmit. That works fine for SSB and CW, but not for AM and FM because of the capacitors in the DAC outputs.

It still seems like the general do-everything answer is to have a crystal filter and divide-by-N synthesizer for the first conversion. I think those are the basic issues. What are your thoughts?

Bob
 
A quick note, un-related to radio architecture. I pushed a new class to the floating-point audio library for clipping of audio, and voice signals in particular. It is called radioVoiceClipper_F32. This is primarily intended for AM, FM and NBFM transmitters (not SSB---for that use the CESSBtransmit class). It includes low-pass filtering and also overshoot removal, giving both good control of the maximum levels and control of the maximum spectral width. It is in the OpenAudio_ArduinoLibrary and also the Floating Point Audio Design Tool. And, there are a couple of examples, as well.
 
Bob, thanks a lot for your detailed answers!
I will start my journey by building the original Charlie Hill pocket FT-8 and see how it works and how that deals with SNR. Have not yet looked too much into the code, though.
OK, I was imprecise in my question on sample rate. I now understood how it works with the FT8 class and I will adjust that in the Convolution SDR code.

For the question on Transmit in AM & FM modes, I think the key is using an audio IF and a quadrature sampling exciter for the TX path. I have answered in more detail on the keithsdr group, but my answer does not come through, because it is handled by the admins in advance!? Not sure what is happening there. Maybe it will appear in a few days after approval by the admin.

Thanks also for the comments on the heterodyning hybrid SDR. That is a new principle to me, so cannot really comment on that in a useful way, I fear, because I have no experience with that yet . . .
 
Back
Top