Audio Library and 4096 point FFT on Teensy 3.6

Status
Not open for further replies.
Hi!

I've just made a quick test with the 4096FFT (using the files goldhausen has provided). It seems to work ok with the ILI9341_t3n Library (using frame buffer) I get 22 FPS.
I'm using the Audio Library and have no issues with using the 4096FFT.

In my project I would like to visualize the audio spectrum pre and post filtering (20 Hz ... 20kHz).
As you can see on the picture, my frequency-axis is not linear. I try to get better resolution especially in the lower frequency range (20 Hz ... 320 Hz), therefore the 4096FFT gets handy with a Frequency Bin resolution of ca. 11 Hz.
Although this works, I agree with DD4WH, the "ZoomFFT" would be a much better approach to solve this "low-frequency" problem.

Is there a "ready to use" ZoomFFT library available that works with the audio library?

AudioAmp_Teeny_3.6_FFT4096.jpg
 
I'm using this to drive an audio spectrum analyzer that outputs to a 128x64 rgb led matrix with 128 bands displayed. I wanted more resolution at the low end (0 to 200hz) and this allowed that to happen.
Since I am only viewing the full audio spectrum as bin intensity, not listening to individual bins, it works for me.
I ran a frequency sweep (20 to 20k) and it displayed the peak properly, so the fft appears to be working correctly.



I get about 10 fps update on the led at 240mhz, so it is slower than I would want, but I get lower frequency discrimination.

This still uses the complex functions for fft (cfft radix4 q15). Looking at the code in cmsis 4.5, it was running at 4096 all of the time, just stepping down for lower sizes. I don't see any difference in the display response characteristics with 4096 vs 1024 (except that caused by having 4x bins) - that confirms to me that it is the same functions being used.
 
Hi!
I've just run into a problem with the 4096FFT. Everything works fine as long as I use only one instance of the 4096FFT.
As soon as I connect a second FFT to the Input "AudioConnection patchCord (i2sInput, 0, fftPre, 0);" both FFTs don't work anymore.
I get some strange artifacts (different Spectrum although I put in the same Signal - Sinewave 380Hz).
Does someone know what could be the reason for this behavior and how I can fix this issue?

Running only one 4096FFT:
Teensy_3.6_1x_4096FFT.jpg

Running two 4096FFTs:
Teensy_3.6_2x_4096FFT.jpg

Initialisation Code:
Code:
AudioMemory(80);
fftPre.windowFunction (AudioWindowHanning4096);
fftPost.windowFunction (AudioWindowHanning4096);

Spectrum Drawing Function:
Code:
for (int x = 0; x < 230; x++)
{
  static int xPrev = 0, xTemp = 0;
  static float pre0 = 0, pre1 = 0, post0 = 0, post1 = 0;
  int16_t bin     = (20 * powf (4, (float) x     / 46)) / 10.75;
  int16_t binPrev = (20 * powf (4, (float) xPrev / 46)) / 10.75;
  int16_t binTemp = (20 * powf (4, (float) xTemp / 46)) / 10.75;
    
  if (bin != binPrev)
  {
    if (x > 3)
    {
      pre0 = getLevel (fftPre.read(binTemp, binPrev), x);
      pre1 = getLevel (fftPre.read(binPrev, bin), x);
      post0 = getLevel (fftPost.read(binTemp, binPrev), x);
      post1 = getLevel (fftPost.read(binPrev, bin), x);
      //tft.drawLine (xPrev + 25, 77 + pre0, x + 25, 77 + pre1, CYAN);
      tft.drawLine (xPrev + 25, 77 + post0, x + 25, 77 + post1, MAGENTA);
    }
    else
    {
      pre0 = getLevel (fftPre.read(1), x);
      pre1 = getLevel (fftPre.read(1, 2), x);
      post0 = getLevel (fftPost.read(1), x);
      post1 = getLevel (fftPost.read(1, 2), x);
      //tft.drawLine (0 + 25, 77 + pre0, 2 + 25, 77 + pre1, CYAN);
      tft.drawLine (0 + 25, 77 + post0, 2 + 25, 77 + post1, MAGENTA);
    }
    xTemp = xPrev;
    xPrev = x;
  }
}
 
Perhaps put in memoryCPUusage monitoring checks - do with One then Both fft_#_4096 enabled

What if the second FFT is made as 1024 or something less intense?

In the boards.txt file is a commented 256 MHz speed option for T_3.6 - uncomment these two lines with removal of '#':
Code:
#teensy36.menu.speed.256=256 MHz (overclock)
#teensy36.menu.speed.256.build.fcpu=256000000
 
Ok, I've changed the second FFT to 1024 and that works fine. But this is not really a solution for me, because I really need both Signal displayed in "high resolution".
After changing the CPU Clock Speed to 256 MHz I got the following Error:
#error "This CPU Clock Speed is not supported by the Audio library";
Do you know the right values for MCLK_MULT and MCLK_DIV for this CPU frequency?
Are there some other critical options I can try to change to get it to work?
 
Post #29 was for diagnostic purposes - the 1024 FFT just shows the system is and can run the 4096 in parallel with 1024, a good sign.

Bad suggestion on the 256 MHz - apparently the math for those clocks hasn't been worked out - not sure if it can be worked out.

Along with @el_supremo's - the FIRST part of the p#29 asked to integrate the "memoryCPUusage monitoring checks" - that will show if there is a memory or CPU cycle deficit. There is an example sketch that exposes and prints those values - some few lines of code copied into loop and printing 1 time per second will give some good info.
 
Here are the results of the CPU and Memory usage:

Only one 4096FFT running:
Code:
fftPre = 0 (0 max), fftPost = 77 (78 max), All = 78.71 (79.56 max), Memory: 19 (37 max)
fftPre = 0 (0 max), fftPost = 0 (78 max), All = 1.43 (79.56 max), Memory: 31 (37 max)
fftPre = 0 (0 max), fftPost = 0 (78 max), All = 1.42 (79.56 max), Memory: 26 (37 max)
fftPre = 0 (0 max), fftPost = 0 (78 max), All = 1.41 (79.56 max), Memory: 24 (37 max)
fftPre = 0 (0 max), fftPost = 0 (78 max), All = 1.41 (79.56 max), Memory: 24 (37 max)
fftPre = 0 (0 max), fftPost = 0 (78 max), All = 1.41 (79.56 max), Memory: 24 (37 max)
fftPre = 0 (0 max), fftPost = 0 (78 max), All = 1.46 (79.56 max), Memory: 24 (37 max)
fftPre = 0 (0 max), fftPost = 0 (78 max), All = 1.46 (79.56 max), Memory: 24 (37 max)
fftPre = 0 (0 max), fftPost = 0 (78 max), All = 1.46 (79.56 max), Memory: 24 (37 max)
fftPre = 0 (0 max), fftPost = 77 (78 max), All = 78.79 (79.56 max), Memory: 19 (37 max)
fftPre = 0 (0 max), fftPost = 0 (78 max), All = 1.43 (79.56 max), Memory: 31 (37 max)
fftPre = 0 (0 max), fftPost = 0 (78 max), All = 1.42 (79.56 max), Memory: 26 (37 max)
fftPre = 0 (0 max), fftPost = 0 (78 max), All = 1.45 (79.56 max), Memory: 23 (37 max)
fftPre = 0 (0 max), fftPost = 0 (78 max), All = 1.44 (79.56 max), Memory: 23 (37 max)
fftPre = 0 (0 max), fftPost = 77 (78 max), All = 78.81 (79.56 max), Memory: 19 (37 max)
fftPre = 0 (0 max), fftPost = 0 (78 max), All = 1.42 (79.56 max), Memory: 31 (37 max)
fftPre = 0 (0 max), fftPost = 0 (78 max), All = 1.42 (79.56 max), Memory: 26 (37 max)
fftPre = 0 (0 max), fftPost = 0 (78 max), All = 1.45 (79.56 max), Memory: 23 (37 max)
fftPre = 0 (0 max), fftPost = 0 (78 max), All = 1.42 (79.56 max), Memory: 19 (37 max)
fftPre = 0 (0 max), fftPost = 0 (78 max), All = 1.43 (79.56 max), Memory: 30 (37 max)
fftPre = 0 (0 max), fftPost = 0 (78 max), All = 1.43 (79.56 max), Memory: 25 (37 max)
fftPre = 0 (0 max), fftPost = 0 (78 max), All = 1.45 (79.56 max), Memory: 23 (37 max)
fftPre = 0 (0 max), fftPost = 0 (78 max), All = 1.45 (79.56 max), Memory: 23 (37 max)
fftPre = 0 (0 max), fftPost = 0 (78 max), All = 1.44 (79.56 max), Memory: 23 (37 max)
fftPre = 0 (0 max), fftPost = 77 (78 max), All = 78.74 (79.56 max), Memory: 19 (37 max)
fftPre = 0 (0 max), fftPost = 0 (78 max), All = 1.42 (79.56 max), Memory: 31 (37 max)
fftPre = 0 (0 max), fftPost = 0 (78 max), All = 1.42 (79.56 max), Memory: 26 (37 max)
fftPre = 0 (0 max), fftPost = 0 (78 max), All = 1.44 (79.56 max), Memory: 23 (37 max)
fftPre = 0 (0 max), fftPost = 77 (78 max), All = 78.77 (79.56 max), Memory: 19 (37 max)

Two 4096FFT running:
Code:
fftPre = 0 (79 max), fftPost = 0 (78 max), All = 1.47 (7.37 max), Memory: 23 (37 max)
fftPre = 0 (79 max), fftPost = 0 (78 max), All = 1.46 (6.73 max), Memory: 23 (37 max)
fftPre = 0 (79 max), fftPost = 0 (78 max), All = 1.47 (6.93 max), Memory: 24 (37 max)
fftPre = 0 (79 max), fftPost = 0 (78 max), All = 1.46 (6.88 max), Memory: 24 (37 max)
fftPre = 0 (79 max), fftPost = 0 (78 max), All = 1.44 (6.98 max), Memory: 25 (37 max)
fftPre = 0 (79 max), fftPost = 0 (78 max), All = 1.44 (7.06 max), Memory: 25 (37 max)
fftPre = 0 (79 max), fftPost = 0 (78 max), All = 1.44 (7.23 max), Memory: 26 (37 max)
fftPre = 0 (79 max), fftPost = 0 (78 max), All = 1.44 (7.37 max), Memory: 26 (37 max)
fftPre = 0 (79 max), fftPost = 0 (78 max), All = 1.44 (7.03 max), Memory: 27 (37 max)
fftPre = 0 (79 max), fftPost = 0 (78 max), All = 1.44 (7.53 max), Memory: 27 (37 max)
fftPre = 0 (79 max), fftPost = 0 (78 max), All = 1.44 (7.05 max), Memory: 28 (37 max)
fftPre = 0 (79 max), fftPost = 0 (78 max), All = 1.44 (7.32 max), Memory: 28 (37 max)
fftPre = 0 (79 max), fftPost = 0 (78 max), All = 1.44 (7.14 max), Memory: 29 (37 max)
fftPre = 0 (79 max), fftPost = 0 (78 max), All = 1.44 (7.09 max), Memory: 29 (37 max)
fftPre = 0 (79 max), fftPost = 0 (78 max), All = 1.44 (6.88 max), Memory: 30 (37 max)
fftPre = 0 (79 max), fftPost = 0 (78 max), All = 1.44 (7.19 max), Memory: 30 (37 max)
fftPre = 0 (79 max), fftPost = 0 (78 max), All = 1.44 (6.99 max), Memory: 31 (37 max)
fftPre = 0 (79 max), fftPost = 0 (78 max), All = 1.46 (6.98 max), Memory: 31 (37 max)
fftPre = 0 (79 max), fftPost = 0 (78 max), All = 1.44 (7.36 max), Memory: 32 (37 max)
fftPre = 0 (79 max), fftPost = 0 (78 max), All = 1.44 (7.34 max), Memory: 32 (37 max)
fftPre = 0 (79 max), fftPost = 0 (78 max), All = 1.44 (7.08 max), Memory: 33 (37 max)
fftPre = 0 (79 max), fftPost = 0 (78 max), All = 1.44 (7.10 max), Memory: 33 (37 max)
fftPre = 0 (79 max), fftPost = 0 (78 max), All = 1.44 (7.33 max), Memory: 34 (37 max)
fftPre = 0 (79 max), fftPost = 0 (78 max), All = 1.44 (7.59 max), Memory: 34 (37 max)
fftPre = 0 (79 max), fftPost = 0 (78 max), All = 1.44 (7.53 max), Memory: 24 (37 max)
fftPre = 0 (79 max), fftPost = 0 (78 max), All = 1.45 (7.73 max), Memory: 25 (37 max)
fftPre = 0 (79 max), fftPost = 0 (78 max), All = 1.45 (7.45 max), Memory: 25 (37 max)
fftPre = 0 (79 max), fftPost = 0 (78 max), All = 1.44 (7.14 max), Memory: 26 (37 max)
fftPre = 0 (79 max), fftPost = 0 (78 max), All = 1.44 (7.55 max), Memory: 26 (37 max)

As it seems, one FFT takes ca. 80% of the CPU usage, therefore it's obvious that 2 FFTs are going to exceed the total CPU usage (more than 100%).
I've changed the AudioMemory to 160 but got the same result. I think memory is not the problem in this case.

Is there a possibility to split the FFT processing functions or optimize the code to increase the efficiency?
 
CPU busy is what I expected/feared ... and shows memory wasn't an issue as allocated. Would be interesting to see those numbers for the combined 4096 with 1024 FFT combo, if 1024 is 4X faster it would explain it fitting in the other 20% leftover.

IIRC the FFT works in (4 sets of 256B buffers? - for 4096 maybe 4X with more buffers in play) steps as buffers fill then does one big hit to get the final calculations. If that is indeed the case and somehow the two could be run out of phase they might share better? That might allow end use processing to also work in turn, or out of phase - but may skew the results if the two need sample time synchronization.

There was also alternate algorithm work by one forum user performing partial calc's on each of those 4 steps to spread the workload - reducing the hit on the final pass.
by @KPC :: pjrc.com/threads/27905-1024-point-FFT-30-more-efficient-(experimental)

There is hope the Teensy 4 will ship soon enough and all else being sufficient the extra processing speed would more than double given the speed increase and more advanced design/resources.

If the code can be shared and run with minimal hardware ( audio board and ili9341 ) perhaps myself or another beta user could run the code that gave the above results to see what comes of CPU usage.
 
better multi-block processing?

Alternatively to partial calculation of very large FFT's (here fft4096) one could run the FFT on a lower priority level (triggered also by SW interrupt). This would need an extension of the audio library, which could be an idea to allow easier multi-block processing.
(&defragster BTW: the 1024 point FFT is done every 8th block, so a 4096 point FFT would be done every 32th block)
 
As far as I can see, there is no need for such a large (and processor-intense) FFT. Of course the T4 could help you, but I am pretty sure that by reducing the load you can do that on a T3.6

Your visualization only uses (at most) 128 points, so you would only need a much smaller FFT, say 256 points [producing a resolution of 172Hz, which is more than enough for your spectrum analyser [for the frequencies above about 700Hz]. The needed resolution for the lower frequencies (< 700Hz) could be produced by a ZoomFFT.

Sorry to say, there is no ready-to-use library for that. You would have to lowpass-filter your audio at the desired frequency (689Hz) and then decimate with your desired factor (decimation-by-32 produces a resolution of 5Hz and corresponds with your cutoff freq at 689Hz, if I did the math correctly). Then perform a 256-point-FFT on the decimated audio [which is now restricted to DC-689Hz and comes at a sample rate of 44.1ksps / 32 = 1.38ksps] and you get 128 real samples with magnitudes in a resolution of 5.4Hz (which is also the maximum number of frames per second for your display).

In a second step you could speed up the frame rate by using an overlap in your input samples [sliding window] and perform both FFTs at 44.1ksps, then you can have the full frame rate AND full resolution. The processor load comparison would then be: FFT4096 OR [2 x FFT256 and lowpass filter] --> the winner in efficiency would be the ZoomFFT, I think.

see here for an explanation:

https://github.com/df8oe/UHSDR/wiki/Spectrum-display-Magnify-mode-=-Zoom-FFT
 
Hallo everybody, I am a new comer.
I am playing around with dual FFT on Teensy 3.6. So far I am happy with the performance. However, the resolution at the lower frequency is a bit low. I would like to try the FFT4096.
I have tried with the sketch by blacktronics in #12, with changes suggested by gohlhausen in #24.
The sketch uploaded ok and seems running ok because I got pages of "0" printing out.
When I changed the FFT256 to FFT4096, the sketch still can be uploaded, but, it is not giving the "0". I suppose the FFT4096 is not working properly.
Anybody can advise me if I have missed anything?
 
Looking at another post for FFT256 the other day I was reminded it does 'naverage = 8;' for that size FFT.

Was wondering is a call to this with a number of 2 or 4 :: averageTogether(uint8_t n)
might slow down the data collection enough to let more than one run?
 
Code:
#include <Audio.h>
#include <avr/power.h>

AudioInputAnalog         adc1(A2);
//AudioAnalyzeFFT4096      fft1;  // fft4096 can complie and load, but not working! 
AudioAnalyzeFFT1024      fft1;  //looks working ok
AudioConnection          patchCord2(adc1, fft1);


void setup() {

Serial.begin(9600);
delay(1000);  
Serial.println("System working   ");  // Just to indicate the system is working

    AudioMemory(16);
//  fft1.averageTogether(8);
}

void loop() { 


   if (fft1.available()) {

    Serial.print("fft1.read(1)     ");
    Serial.println(fft1.read(1));

    Serial.print("fft1.read(100)   ");
    Serial.println(fft1.read(100));
 
   }

 /* 
  if (fft1.available()) {
    for (uint16_t i = 0; i < 4096; i += 1) {
      int32_t n = fft1.read(i);
      Serial.print(n);
      Serial.print(" ");
    }
    Serial.println();
  }
*/
   
}

Thank you for your reply defragster.
To make the thing simple, I have commented out the averageTogether(uint8_t n) and print out just the frequency channel no "1" and no "100". When selecting fft4096, he sketch only print out the test print in the setup. The fft values are not being printed. (Print out ok when selecting fft1024)
I must have missed something.
 
If a single FFT4096 is not working like in prior posts then something must be missing/wrong AFAIK.

Using 'averageTogether' with a larger number was only to help when trying multiple large FFT's - it will make the data collection period take longer between updates to the FFT - and 8 is a big number.
 
In order to eliminate any wrong doings, I have reinstalled the Arduino (v 1.8.9) and Teensyduino (v 1.47). I have also done the followings in :)\Program Files\Arduino\hardware\teensy\avr\libraries\audio) :

1. Copied the the Analyze_fft4096.cpp and Analyze_fft4096.h file from #24.

2. Replaced the files Audio.h ; data_windows.c and keywords.txt by those in #24.

Unfortunately, the system still not working on FFT4096 (The sketch can compile, upload and run, because it can print the test print in the setup void, it is just not running the fft4096. It runs ok on fft1024.). Anybody can advise if I have missed anything?

By the way, I am still working on the Windows XP system.
 
I have tried various method, still cannot be able to find out why the fft4096 doesn't work. I need help. I should be very grateful if anybody can give me a direction to go ahead.
 
A couple of suggestions:
1) In the original post it was mentioned that it was only the lower part of the spectrum that is of interest. You might try a "zoom FFT" approach (google it), and examine only what you want. This would allow you to use a smaller FFT and retain the resolution you want.

2) In order to keep the processing time low within the update time of the Audio library, I often extract the data from the internal data stream and process it in the loop() as a lower priority task. To that end I've written a set of "audioGrabbers" that assemble a given length block of Audio data (say 256 complex data elements, or 512 real data points) and then signal that a block is available. In loop() I check the "new data available" flag and then "grab" the data, and process it. I double-buffer it so that there are no data collisions while grabbing occurs. For example, I use this method for spectral analysis in my SDR (software-defined-radio) work. I currently don't have a 1024 length grabber written, but I'd be happy to throw one together for you...
 
Thank you so much for your suggestion, Derek.

1) As according to gohlhausen in #24 and FLEOX in #30, it looks to me that FFT4096 works in T3.6. I think I must have make some silly simple mistakes that makes the system doesn't work. I very much like to find out the reason why. If there is any speed/resources problem, I would not be hesitated to change to T4.0.

2) The FFT is working on the linear frequencies and unfortunately, if we are working on audio system, our ears are responding to the log frequencies. It would be ideal if using fft1024 and compensated by "zoom FFT", with "zoom FFT" extracting the lower frequencies and the fft1024 extracting higher frequencies. I should like to study more to find out if the two system can use the same batch of samples. (e.g sampling speed at 44.1K and use 1024 samples batch). I should be grateful if you could recommend somewhere I can find the information.

3) I am sorry to say that I am working only on the Arduino/Teensyduino, together with the Audio Design Tool. I am innocent to C and C++. (I think I should study these languages to improve myself). I am not sure if I can manage the technical involvements in you "audioGrabbers" suggestion.

4) What I am trying to make a system similar to a RTA (real time analyser). The difference between the existing RTA is that I want both the number of channels and the frequencies included in each channel to be programmable.
 

Rather than using a single FFT, you might like to consider using the lower portions of multiple FFTs. The low frequencies would be based on possibly the last one second of audio input, but for higher frequencies you can use shorter periods, halving the sample length every octave. This means the bins at higher frequencies are wider apart and collect more of the input without adding bins together.

This is a simplified version of what a Logarithmic FFT does, and gives significantly faster calculation times.
 
Thank you so much for your suggestion, Mike.

The simplified version of Logarithmic FFT seems good. However, FLEOX did not give details of how to achieve the result.

My theoretical knowledge on this issue is almost zero (sorry to say that). I choose the fft4096 is because someone has already done it and it will be easier just to follow. I didn't understand why I couldn't get it work in my system.

If you can kindly give me more details on how to realize the simplified version of Logarithmic FFT, I would definitely want to have a trial.
 
Hi,

sounds interesting, that "logarithmic FFT". As far as I can see, it must be quite similar to the "ZoomFFT"!? If you use longer input periods, your sample rate would have to be lowered and you would have to lowpass-filter before downsampling. So thats the same principle as the ZoomFFT, what do you think? Code for the ZoomFFT can be found in the Teensy Convolution SDR (lines 6702ff). I achieve frequency resolution in the sub-Hz-region by Zooming-by-4096, for example (with an underlying 512-point-FFT). However, as the decimation factor of the ARM CMSIS decimation function is limited, you would have to decimate in two steps, but that also lowers the restriction for your lowpass filters. And for increasing temporal resolution (faster screen updates), you could use overlapping input buffers.

See my post #25 and #36 in this thread for explanations and link to open source code for the ZoomFFT on the Teensy 3.6.

I really think you should not do a 4096-point-FFT for that purpose, it is a waste of processing power.

All the best,

Frank DD4WH
 
Thank you so much to Frank and to all other members for the responses.

I don't know anything about logarithmic FFT. I need to study more.

As according to my understanding, ZoomFFT restricted in the Zoomed frequency region only. Any frequency outside the zoomed region will be filtered off, therefore, if I still want the higher frequency components, I need another fft loop (with different sampling frequency) running in parallel.

If there are 2 fft loops to be run in parallel, I have the following idea:
1) To extract higher frequency components, use fft256, with a sampling frequency of 44.1Khz. I can get a resolution of around 170 Hz at the high frequency region, which is good enough.
2) To extract the lower frequency components, use fft256, with sampling frequency of around 2.2 Khz. I can get a resolution of around 8 Hz, which is very good already.

The question is whether the T3.6 can support 2 fft loop running in parallel with different sampling frequency? I understand that T3.6 has 2 ADCs, may be we can use different sampling frequency for each ADC. Anybody has any idea?
 
Your two points are exactly the way to go. BTW, point 2) is in fact the ZoomFFT ;-).

You do not need two ADCs with different sample rates. Just use one ADC with 44.1kHz, and do this:

1.) normal 256-point-FFT with the original audio stream @44.1kHz sample rate --> gives enough resolution for high frequencies above about 700Hz

2.) You should decimate the data for point 2), which means two steps:

* lowpass-filter the audio stream (with a 689Hz filter, maybe a biquad IIR filter)
* downsample-by-32 --> take only every 32th sample
* perform the 256-point FFT on this decimated audio stream
* the 128 FFT results will have high frequency resolution

44.1kHz / 32 = 1.378kHz sample rate, which means you can use frequencies up to 689Hz (Nyquist)

Resolution is 1.378kHz / 256bins = 5.4Hz (or 689Hz/128 FFT magnitude values)

Or you use decimation-by-16 which means a resolution of . . . but you can calculate that by yourself.

For further understanding of this concept of the ZoomFFT, I recommend reading through the links on this page (scroll down the page):

https://github.com/df8oe/UHSDR/wiki/Spectrum-display-Magnify-mode-=-Zoom-FFT
 
Thank you so much for your quick response, Frank.

Looks like a good solution. I need time to digest. I shall let you know the result, but it takes time.
 
Status
Not open for further replies.
Back
Top