-
Notifications
You must be signed in to change notification settings - Fork 11
/
Teensy_SDR.ino
2903 lines (2593 loc) · 107 KB
/
Teensy_SDR.ino
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/* Teensy SDR
* version 2016_02_22
* on my new laptop
* DD4WH mods included
*
* Experiment: freq_conv einbauen --> ok!
* AM switchoff (passthrough) in other modes --> ok! (sideband suppression superb)
* now only 47% processor load in SSB modes !!!
*
* ***************************************************
*
* this script only works with modified mixer.h in Audio library
* allow for negative gain !
*
*****************************************************
* processor load:
* SSB: 50% / 53 % (mem 12 / 19)
* AM: 63% / 66 % (mem 14 / 21)
* after elimination of biquad 3 & 4
* SSB: 45% / 47% (mem 14 / 19)
* AM: 57% / 60% (mem 14 / 19)
*
New features by DD4WH
+ spectrum display now with 44.1kHz bandwidth, uses complex FFT256IQ library by kpc
+ spectrum display with pixels instead of bars, LPF implemented for smooth appearance
+ added AM demodulation
+ added pseudo stereo AM
+ added DSB demodulation
+ added passband tuning, meaning easy adjustment of different bandwidths for upper and lower sideband (in DSB and StereoAM mode)
+ added automatic snap mode, which automatically searches the carrier in DSB mode when in 100Hz tuning step Tuning mode --> uses three-point-quadratic interpolation (thanks vladn)
+ added quadrature oscillators in the I and Q paths in order to translate IQ to baseband for demodulation, so that all filters work in baseband now (thanks Clint KA7OEI)
+ Hilbert and FIR filter bandwidths 1.8kHz, 2.6kHz, 3.6kHz, 4.4kHz, 6.5kHz, 8kHz, 11kHz --> all combined with adjustable IIR
+ more IIR filter bandwidths 1.8kHz, 2.3kHz, 2.6kHz, 3.1kHz, 3.6kHz, 4.0kHz, 4.4kHz, 5.4kHz, 6.5kHz, 8kHz, 11kHz
+ filter "switching" done automatically by smooth adjusting bandwidth with encoder (IIR and FIR/Hilberts automatically switched)
+ biquad 1: fixed bandwidths with postIIR filter (8th order IIR --> 4 cascaded biquad stages, inverted Chebychev)
+ (manually adjustable notch filter (8th order IIR biquad filter))
+ calibrated graphical S-meter implemented, S-value independent of analog gain ("RF gain") setting and digital volume control
+ tone control: bass & treble (postAudioprocessor)
+ recorder included, records audio from the SDR radio to RAW-files on the SD-card - up to 255 files, ca. 8MB per minute, playback also possible
+ tune step for LW & MW 9kHz, all other bands 5kHz
+ Real Time Clock integrated (backed up by battery)
+ date adjust integrated
+ frequency limits 12kHz - 36MHz implemented by using the new Si5351 library by Jason --> now SAQ on 17.1kHz is receivable ;-)
+ new 2015 version of Jason´s Si5351 library included: now tunes down to 12kHz, but below that Si5351 crashes, requires new boot
+ also new Si5351 library needed correction_factor for frequencies
+ Menu addition: adjust correction_constant for Si5351
+ Menu addition: adjust correction_factor for Si5351
+ Menu addition: Save settings to EEPROM
+ Menu addition: Load settings from EEPROM
+ bands array now also holds mode & USB bandwidth & LSB bandwidth for each band (and saved by EEPROM)
+ band is now global variable and is saved in EEPROM --> after load switches to last band saved
+ LPF automatic relay switching --> my hardware has 5 switchable elliptic Low Pass Filters: these are really needed for serious receive to supress 3rd, 5th, 7th and higher order harmonics
+ now five buttons (two additional ones)
+ added BAND down function, so buttons 1 and 2 now function as BAND DOWN and BAND UP
+ manual calibration of I + Q amplitude & phase --> phase adjustment mixes a little amount of I into Q or vice versa before passing the Hilbert Filters
+ button 5: Menu "RF gain" = manually adjust analog codec gain = "RF gain" in front of ADC (lineInLevel adjustable from 0-15 --> 0dB to 22.5dB in steps of 1.5dB)
+ Menu addition: choose among eleven different FFT Window Functions
+ added to EEPROM load and save: IQ amplitude & IQ phase calibrations, RF gain, mode, bandwidth USB, bandwidth LSB, FFT Window Function, LPF coeff for spectrum display
+ separate variables & voids: mode & audio bandwidth
+ show voltage Vcc (Menu2 entry) and check every 5 secs and warn if < 11 volts
+ larger bandwiths for IIR filters in AM mode to allow for sideband-selected AM reception
+ Freq_conv.cpp mit receiveWritable
+ very narrow manually adjustable notch filter with graphical indicator
+ frequency display: update only those frequency digits that have changed during tuning
+ time display: update only digits that have changed
Todo
+ sideband selected AM by shifting IF with an offset --> implemented in menu passband tuning
+ decimate and interpolate for biquad1 & biquad2 --> is it worth it? We already have reduced processor load to 47% . . .
+ LMS noise reduction, does it work even without decimation / interpolation ?
+ VFO A and B
+ switch between local time & UTC
+ Menu point: reset to factory settings
+ memories for stations with name & time read from txt.file on SD card
* simple software define radio using the Softrock transceiver
* the Teensy audio shield is used to capture and generate 16 bit audio
* audio processing is done by the Teensy 3.1
* simple UI runs on a 160x128 color TFT display - AdaFruit or Banggood knockoff which has a different LCD controller
* Copyright (C) 2014, 2015 Rich Heslip [email protected]
* History:
* 4/14 initial version by R Heslip VE3MKC
* 6/14 Loftur E. Jónasson TF3LJ/VE2LJX - filter improvements, inclusion of Metro, software AGC module, optimized audio processing, UI changes
* 1/15 RH - added encoder and SI5351 tuning library by Jason Milldrum <[email protected]>
* - added HW AGC option which uses codec AGC module
* - added experimental waterfall display for CW
* 3/15 RH - updated code to Teensyduino 1.21 and audio lib 1.02
* - added a lot of #defines to neaten up the code
* - added another summer at output - switches audio routing at runtime, provides a nice way to adjust I/Q balance and do AGC/ALC
* - added CW I/Q oscillators for CW transmit mode
* - added SSB and CW transmit
* - restructured the code to facilitate TX/RX switching
* Todo:
* clean up some more of the hard coded HW and UI stuff
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define ver "v 2016.02.22"
#include <Time.h>
#include <TimeLib.h>
#include <Metro.h>
#include <Audio.h>
#include <Wire.h>
#include <SD.h>
#include <Encoder.h>
#include <EEPROM.h>
#include <si5351.h>
#include <Bounce.h>
//#include <Bounce2.h>
#include <Adafruit_GFX.h> // LCD Core graphics library
//#include <Adafruit_QDTech.h>// 1.8" TFT Module using Samsung S6D02A1 chip
#include <Adafruit_ST7735.h> // Hardware-specific library
#include <SPI.h>
#include "analyze_fft256iq.h" // new complex FFT library for full bandwidth spectrum from I & Q --> 44.1kHz
#include "AM_demod.h"
#include "freq_conv.h"
#include "filters.h"
#include "display.h"
#include "iir_18.h"
#include "iir_23.h"
#include "iir_26.h"
#include "iir_31.h"
#include "iir_36.h"
#include "iir_40.h"
#include "iir_44.h"
#include "iir_54.h"
#include "iir_65.h"
#include "iir_80.h"
#include "iir_110.h"
void sinewave(void);
extern void agc(void); // RX agc function
extern void setup_display(void);
extern void init_display(void);
//extern void show_spectrum(void); // spectrum display draw
extern void show_spectrum(float line_gain, float LPFcoeff); // spectrum display draw
extern void show_waterfall(void); // waterfall display
//extern void show_bandwidth(int filterwidth); // show filter bandwidth
extern void show_radiomode(String mode); // show filter bandwidth
extern void show_band(String bandname); // show band
extern void show_frequency(long int freq); // show frequency
extern void show_tunestep (String S);
extern void show_bandwidth (int M, long int FU, long int FL);
extern void show_notch (int notchF, int MODE);
time_t getTeensy3Time()
{
return Teensy3Clock.get();
}
#define Schedule_length 3
//#define DEBUG
//#define DEBUG_TIMING // for timing execution - monitor HW pin
// SW configuration defines
// don't use more than one AGC!
//#define SW_AGC // define for Loftur's SW AGC - this has to be tuned carefully for your particular implementation
// codec hardware AGC works but it looks at the entire input bandwidth
// ie codec HW AGC works on the strongest signal, not necessarily what you are listening to
// it should work well for ALC (mic input) though
//#define HW_AGC // define for codec AGC
// #define CW_WATERFALL // define for experimental CW waterfall - needs faster update rate
#define AUDIO_STATS // shows audio library CPU utilization etc on serial console
// band selection stuff
struct band {
long freq; // frequency in Hz
// unsigned long freq; // frequency in Hz
String name; // name of band
int mode; // sideband & bandwidth
int bandwidthU;
int bandwidthL;
int RFgain;
};
// Estebans defs
//static int16_t IF_FREQ = 5515; // IF Oscillator frequency
int16_t IF_FREQ = 5515; // IF Oscillator frequency
float s_old = 0;
//static float32_t Osc_Q_buffer_i_f[AUDIO_BLOCK_SAMPLES];
//static float32_t Osc_I_buffer_i_f[AUDIO_BLOCK_SAMPLES];
float32_t Osc_Q_buffer_i_f[AUDIO_BLOCK_SAMPLES];
float32_t Osc_I_buffer_i_f[AUDIO_BLOCK_SAMPLES];
q15_t Osc_Q_buffer_i[AUDIO_BLOCK_SAMPLES];
q15_t Osc_I_buffer_i[AUDIO_BLOCK_SAMPLES];
//static bool flag = 0;
bool flag = 0;
bool freqconv_sw=1; // 1 = frequency conversion on; 0 = freqconv off
bool AM_pass = 1; // 1 = Am demodulation on; 0 = off
q15_t last_dc_level = 0;
//#define IF_FREQ 5515 //11029 //5515 // IF Oscillator frequency, already defined above!!
long calibration_factor = 10000000 ;// 10002285;
long calibration_constant = 3500;
unsigned long long hilfsf;
#define F_MAX 36000000 + IF_FREQ
#define F_MIN 12000 + IF_FREQ
#define BAND_LW 0 // these can be in any order but indexes need to be sequential for band switching code
#define BAND_MW 1
#define BAND_120M 2
#define BAND_90M 3
#define BAND_75M 4 // these can be in any order but indexes need to be sequential for band switching code
#define BAND_60M 5
#define BAND_49M 6
#define BAND_41M 7
#define BAND_31M 8
#define BAND_25M 9
#define BAND_22M 10
#define BAND_19M 11
#define BAND_16M 12
#define BAND_15M 13
#define BAND_13M 14
#define BAND_11M 15
#define FIRST_BAND BAND_LW
#define LAST_BAND BAND_15M
#define NUM_BANDS 16
// radio operation mode defines used for filter selections etc
#define modeAM 0
#define modeUSB 1
#define modeLSB 2
#define modeDSB 3
#define modeStereoAM 4
#define modeSAM 5
#define firstmode modeAM
#define lastmode modeStereoAM
#define startmode modeAM
#define MAX_BANDWIDTH 11000
// f, band, mode, bandwidth, RFgain
struct band bands[NUM_BANDS] = {
153000-IF_FREQ,"LW", modeAM, 3600,3600,0,
531000-IF_FREQ,"MW", modeAM, 3600,3600,0,
2485000-IF_FREQ,"120M", modeAM, 3600,3600,0,
3500000-IF_FREQ,"90M", modeLSB, 3600,3600,6,
3905000-IF_FREQ,"75M", modeAM, 3600,3600,4,
4750000-IF_FREQ,"60M", modeAM, 3600,3600,7,
5910000-IF_FREQ,"49M", modeAM, 3600,3600,0,
7120000-IF_FREQ,"41M", modeAM, 3600,3600,0,
9420000-IF_FREQ,"31M", modeAM, 3600,3600,0,
11735000-IF_FREQ,"25M", modeAM, 3600,3600,0,
13570000-IF_FREQ,"22M", modeAM, 3600,3600,0,
15140000-IF_FREQ,"19M", modeAM, 3600,3600,0,
17480000-IF_FREQ,"16M", modeAM, 3600,3600,0,
18900000-IF_FREQ,"15M", modeAM, 3600,3600,0,
21450000-IF_FREQ,"13M", modeAM, 3600,3600,0,
25670000-IF_FREQ,"11M", modeAM, 3600,3600,0
};
#define STARTUP_BAND BAND_LW //
int band = STARTUP_BAND;
//SPI connections for 1.8" display
const int8_t sclk = 5;
const int8_t mosi = 4;
const int8_t cs = 2;
const int8_t dc = 3;
const int8_t rst = 1;
const int8_t MenuSW = 20; // button 4 = menu switch: encoder tune or encoder BW adjust
const int8_t ModeSW = 26; // button 3 = USB/LSB & bandwidth, ehemals pin 20
const int8_t BandDOWNSW = 21; // button 1 = band selector down
const int8_t BandUPSW = 24; // button 2 = band selector up
const int8_t TuneSW = 8; // Optical Encoder pushbutton
const int8_t Button5 = 25; // Menu2 switch
const int8_t Band1 = 29; // band selection pins for LPF relays, used with 2N7000: HIGH means LPF is activated
const int8_t Band2 = 30; // always use only one LPF with HIGH, all others have to be LOW
const int8_t Band3 = 31;
const int8_t Band4 = 32;
const int8_t Band5 = 33; // attention, special pin, only use as output pin!
const int8_t VoltCheck = 28; // DAC for voltage check
// definitions for cursor positions display setup
const int8_t pos_x_date = 100;
const int8_t pos_y_date = 119; //91;
const int8_t pos_x_time = 0;
const int8_t pos_y_time = 119;//91;
const int8_t pos_x_station = 0;
const int8_t pos_y_station = 0;
const int8_t pos_x_frequency = 39; //100
const int8_t pos_y_frequency = 59; //119
const int8_t pos_x_menu = 100;
const int8_t pos_y_menu = 119;
#define pos 50 // position of spectrum display, has to be < 124 --> also to be defined in display.cpp
Adafruit_ST7735 tft = Adafruit_ST7735(cs, dc, mosi, sclk, rst); // soft SPI
#define BACKLIGHT 0 // backlight control signal
// Optical Encoder connections
Encoder tune(16, 17);
// clock generator
Si5351 si5351;
#define MASTER_CLK_MULT 4 // QSD frontend requires 4x clock
// various timers
Metro five_sec=Metro(5000); // Set up a 5 second Metro
Metro one_sec = Metro (1000);
Metro ms_100 = Metro(100); // Set up a 100ms Metro
Metro ms_50 = Metro(50); // Set up a 50ms Metro for polling switches
Metro lcd_upd =Metro(4); // Set up a Metro for spectrum display updates
Metro CW_sample =Metro(10); // Set up a Metro for LCD updates
Metro MP3_Smetertimer = Metro(200);
#ifdef CW_WATERFALL
Metro waterfall_upd =Metro(25); // Set up a Metro for waterfall updates
#endif
#define TUNE_STEP1 5000 // shortwave
#define TUNE_STEP2 1 // fine tuning
#define TUNE_STEP3 100 //
#define first_tunehelp 1
#define last_tunehelp 3
int tunehelp = 1;
int tunestep = TUNE_STEP1;
String tune_text="Fast Tune";
// Menus !
#define TUNING 0
#define BWADJUST 1
#define NOTCH 2
#define RECORDING 3
#define CALIBRATIONCONSTANT 4
#define IQADJUST 5
#define IQPHASEADJUST 6
#define SAVETOEEPROM 7
#define LOADFROMEEPROM 8
#define TIMEADJUST 9
#define DATEADJUST 10
#define CALIBRATIONFACTOR 11
#define FFTWINDOW 12
#define LPFSPECTRUM 13
#define BW_step 200
#define first_menu TUNING
#define last_menu LPFSPECTRUM
#define start_menu TUNING
int Menu = TUNING;
String menu_text = "Tuning";
// Menu2 !
#define SNAP 1
#define PASSBAND 2
#define RFGAIN 3
#define BASS 4
#define TREBLE 5
//#define MP3 5
#define VOLTAGE 6
#define VERSION 7
//#define NOTCH 8
#define first_menu2 TUNING
#define last_menu2 VERSION //NOTCH
#define start_menu2 TUNING
int Menu2 = start_menu2;
float bass = 0.4;
float bass_help = bass * 100;
float treble = 0.0;
float treble_help = treble *100;
int notchF = 200; // if 200, notch is off, if >= 400, notch is on
int notchQ = 10;
//bool notchswitch = 0; // 0 = notch filter off; 1 = notchfilter on
//bool notchside = 0; // 0 = LSB; 1 = USB
float LPFcoeff = 0.3; // used for low pass filtering the spectrum display
float LPFcoeffhelp = LPFcoeff * 100;
int passbandBW = 0;
int maximum = 0;
int maxbin = 0;
int Lbin = 0;
int Ubin = 0;
int links = 0;
int rechts = 0;
float delta = 0;
float binBW = AUDIO_SAMPLE_RATE_EXACT / 256.0;
float bin1 = 0;
float bin2 = 0;
float bin3 = 0;
int posbin = 159; // for Rx above IF
// int posbin = 95; // for Rx below IF
int helpmin; // definitions for time and date adjust - Menu
int helphour;
int helpday;
int helpmonth;
int helpyear;
int helpsec;
uint8_t hour10_old;
uint8_t hour1_old;
uint8_t minute10_old;
uint8_t minute1_old;
uint8_t second10_old;
uint8_t second1_old;
bool timeflag = 0;
float Q_gain=1000;
float I_gain=1000;
float I_help = 1.0;
float Q_help = 1.0;
float Q_in_I = 0.000;
float Q_in_I_help = 0;
float I_in_Q = 0.000;
float I_in_Q_help = 0;
float IQcounter = 0;
#define INITIAL_VOLUME 0.8 // 0-1.0 output volume on startup
float vol = INITIAL_VOLUME;
//int line_in_gain = 0; // 0 --> 0 dB, 15 --> 22.3 dB of analog gain in front of the ADC
//int line_in_gain_help;
int Window_FFT = 0;
String FFT_STRING;
float Volt;
int RECORDFLAG = 0;
/* ############################################################
* definitions for MP3 Player & Recorder
* MP3-Player (Frank Bösing) did not work properly, memory issues ??
* it worked as standalone without SDR radio and with 1024FFT
* ###########################################################
*/
int audiomem = 0;
int Rec_Mode = 0; // Recorder: 0 = stopped, 1 = recording, 2 = playing
File frec; // audio is recorded to this file first
int playfile = 0;
//int recfile = -1;
// buttons in RECORD Mode
Bounce bouncer1 = Bounce(BandUPSW, 50); //play
Bounce bouncer3= Bounce(BandDOWNSW, 50); //record
Bounce bouncer2 = Bounce(ModeSW, 50);//stop & switch to radio
Bounce bouncer4 = Bounce(MenuSW, 50);//previous track
Bounce bouncer5 = Bounce(Button5, 50); //next track
/* ############################################################
* end of definitions for MP3 Player
*
* ###########################################################
*/
// audio definitions
// Create the Audio components. These should be created in the
// order data flows, inputs/sources -> processing -> outputs
//
AudioInputI2S audioinput; // Audio Shield: mic or line-in
AudioMixer4 audio_in_I;
AudioMixer4 audio_in_Q;
AudioEffectFreqConv FreqConv;
AMDemod AM;
// FIR filters
AudioFilterFIR FIR_I;
AudioFilterFIR FIR_Q;
// IIR filters (= biquads)
AudioFilterBiquad biquad1;
AudioFilterBiquad biquad2;
AudioFilterBiquad biquad3;
AudioFilterBiquad biquad4;
AudioMixer4 Summer; // Summer (add inputs)
AudioAnalyzeFFT256IQ myFFT; // Spectrum Display complex FFT
/*
AudioSynthWaveform cosIF; // Local Oscillator
AudioSynthWaveform sinIF; // Local Oscillator
//AudioAnalyzePeak MP3_peak; // does not seem to work: bug internally in AudioAnalyzePeak ???
AudioEffectMultiply multiply_I_cos; // Mixer (multiply inputs)
AudioEffectMultiply multiply_Q_sin; // Mixer (multiply inputs)
AudioEffectMultiply multiply_I_sin; // Mixer (multiply inputs)
AudioEffectMultiply multiply_Q_cos; // Mixer (multiply inputs)
*/
AudioRecordQueue recorder; // to record audio from the radio to SD card
AudioMixer4 recmix;
//AudioMixer4 add_substract_I; // belongs to the complex NCO
//AudioMixer4 add_substract_Q; // belongs toe the complex NCO
AudioMixer4 USB;
AudioMixer4 LSB;
AudioMixer4 mixleft;
AudioMixer4 mixright;
AudioOutputI2S audioOutput; // Audio Shield: headphones & line-out
//AudioPlaySdWav playWAV1;
//AudioPlaySdMp3 playMp31;
//AudioPlaySdAac playAac1;
AudioPlaySdRaw playSd; // playback recorded files in RAW format
AudioControlSGTL5000 audioShield; // Create an object to control the audio shield.
//---------------------------------------------------------------------------------------------------------
// Create Audio connections to build a software defined Radio Receiver
//
// audio input and IQ amplitude and phase correction
AudioConnection c5(audioinput, 0, audio_in_I, 0);
AudioConnection c6(audioinput, 1, audio_in_Q, 0);
AudioConnection c7(audio_in_I, 0, audio_in_Q, 1);
AudioConnection c8(audio_in_Q, 0, audio_in_I, 1);
// complex FFT for spectrum display
AudioConnection c23(audioinput, 0, myFFT, 0);
AudioConnection c24(audioinput, 1, myFFT, 1);
AudioConnection c3(audio_in_I, 0, FreqConv, 0);
AudioConnection c4(audio_in_Q, 0, FreqConv, 1);
/*
// Quadrature oscillator for frequency translation of the I and Q into baseband
AudioConnection c3(audio_in_I, 0, multiply_I_cos, 0);
AudioConnection c4(audio_in_Q, 0, multiply_Q_cos, 0);
AudioConnection c9(audio_in_I, 0, multiply_I_sin, 0);
AudioConnection c10(audio_in_Q, 0, multiply_Q_sin, 0);
AudioConnection c11(cosIF, 0, multiply_I_cos, 1);
AudioConnection c12(cosIF, 0, multiply_Q_cos, 1);
AudioConnection c13(sinIF, 0, multiply_Q_sin, 1);
AudioConnection c14(sinIF, 0, multiply_I_sin, 1);
AudioConnection c15(multiply_I_cos, 0, add_substract_I, 0);
AudioConnection c16(multiply_Q_sin, 0, add_substract_I, 1);
AudioConnection c17(multiply_I_sin, 0, add_substract_Q, 0);
AudioConnection c18(multiply_Q_cos, 0, add_substract_Q, 1);
// Main filters: either Hilberts with +45/-45 degrees or standard FIR filters
AudioConnection c19(add_substract_I, 0, FIR_I,0);
AudioConnection c20(add_substract_Q, 0, FIR_Q,0);
//AudioConnection c19(add_substract_Q, 0, FIR_I,0);
//AudioConnection c20(add_substract_I, 0, FIR_Q,0);
*/
AudioConnection c19(FreqConv, 0, FIR_I,0);
AudioConnection c20(FreqConv, 1, FIR_Q,0);
// Standard AM demodulator
AudioConnection c21(FIR_I, 0, AM, 0);
AudioConnection c22(FIR_Q, 0, AM, 1);
AudioConnection c25a(AM, 0, USB, 2);
AudioConnection c25b(AM, 0, LSB, 2);
// connections between I and Q signals and the AM/LSB/USB/DSB switches (mixers)
AudioConnection c26a(FIR_I ,0, USB, 0);
AudioConnection c26b(FIR_I ,0, LSB, 0);
AudioConnection c27a(FIR_Q ,0, USB, 1);
AudioConnection c27b(FIR_Q ,0, LSB, 1);
// IIR filters separately for left and right channel (to enable "stereo AM" = pseudostereo DSB reception)
AudioConnection c28a(USB, 0, biquad1, 0); // right channel
AudioConnection c29a(biquad1, 0, biquad3, 0);
AudioConnection c28b(LSB, 0, biquad2, 0); // left channel
AudioConnection c29b(biquad2, 0, biquad4, 0);
//mp3
//AudioConnection patch1(playMp31,0,mixleft,0);
//AudioConnection patch2(playMp31,1,mixright,0);
//aac
//AudioConnection patch1a(playAac1, 0, mixleft, 1);
//AudioConnection patch2a(playAac1, 1, mixright, 1);
// play RAW files from SD (in mono)
AudioConnection patch3(playSd, 0, mixleft, 3);
AudioConnection patch4(playSd, 0, mixright, 3);
// outputs from the IIR filters go to both mixers (left & right)
AudioConnection c30a(biquad3,0, mixright, 0);
AudioConnection c30b(biquad3,0, mixleft, 0);
AudioConnection c31a(biquad4,0, mixright, 1);
AudioConnection c31b(biquad4,0, mixleft, 1);
// mix left & right channel for recorder
AudioConnection c101 (mixleft, 0, recmix, 1);
AudioConnection c102a (mixright, 0, recmix, 0);
// connect recorder to recmixer
AudioConnection c102b (recmix, 0, recorder, 0);
// stereo output connected to mixright & mixleft
AudioConnection c32(mixright,0, audioOutput, 1);
AudioConnection c33(mixleft,0, audioOutput, 0);
//---------------------------------------------------------------------------------------------------------
void setup()
{
Serial.begin(9600); // debug console
#ifdef DEBUG
while (!Serial) ; // wait for connection
Serial.println("initializing");
#endif
setSyncProvider(getTeensy3Time);
//setTime(19,55,0,3,5,2015);
pinMode(BACKLIGHT, INPUT_PULLUP); // yanks up display BackLight signal
pinMode(ModeSW, INPUT_PULLUP); // USB/LSB switch
pinMode(BandUPSW, INPUT_PULLUP); //
pinMode(BandDOWNSW, INPUT_PULLUP); //
pinMode(TuneSW, INPUT_PULLUP); // tuning rate = high
pinMode(MenuSW, INPUT_PULLUP); //
pinMode(Button5, INPUT_PULLUP); //
pinMode(Band1, OUTPUT); // LPF switches
pinMode(Band2, OUTPUT); //
pinMode(Band3, OUTPUT); //
pinMode(Band4, OUTPUT); //
pinMode(Band5, OUTPUT); //
// Audio connections require memory to work. For more
// detailed information, see the MemoryAndCpuUsage example
if (!digitalRead(BandDOWNSW)) {AudioMemory(21); audiomem = 21;} // this seems to be the figure that is the cause of the sometimes arbitrary loss of mirror rejection!? 32 does not work
else {AudioMemory(90); audiomem = 100;}
// SDR mode needs a max of 21, M4A playback needs a max of 11, recording needs > 70
// Enable the audio shield and set the output volume.
audioShield.enable();
audioShield.volume(INITIAL_VOLUME/2);
audioShield.audioPostProcessorEnable(); // enables the DAP chain of the codec post audio processing before the headphone out
#ifdef HW_AGC
/* COMMENTS FROM Teensy Audio library:
Valid values for dap_avc parameters
maxGain; Maximum gain that can be applied
0 - 0 dB
1 - 6.0 dB
2 - 12 dB
lbiResponse; Integrator Response
0 - 0 mS
1 - 25 mS
2 - 50 mS
3 - 100 mS
hardLimit
0 - Hard limit disabled. AVC Compressor/Expander enabled.
1 - Hard limit enabled. The signal is limited to the programmed threshold (signal saturates at the threshold)
threshold
floating point in range 0 to -96 dB
attack
floating point figure is dB/s rate at which gain is increased
decay
floating point figure is dB/s rate at which gain is reduced
*/
audioShield.autoVolumeControl(2,1,0,-30.0,3.0,70.0); // see comments above
audioShield.autoVolumeEnable();
#endif
// initialize the TFT and show signon message etc
SPI.setMOSI(7); // set up HW SPI for use with the audio card - alternate pins
SPI.setSCK(14);
init_display();
setup_display();
/* tft.fillRect(0,pos_y_frequency, 160,16,BLACK);
tft.setCursor(0,pos_y_frequency);
tft.setTextSize(2);
tft.setTextColor(WHITE);
tft.print("memory ");
tft.print(audiomem);
delay(1000);
tft.setTextSize(1);
setup_display();
*/
// If red button switch is pressed on boot, factory settings are loaded as start settings
//delay (1000);
//if (digitalRead(TuneSW))
EEPROMLOAD(); // get saved frequencies, modes and calibration from EEPROM
// set up initial band and frequency
show_band(bands[band].name);
show_tunestep(tune_text);
// here startup message
tft.fillRect(pos_x_frequency,pos_y_frequency, 160-pos_x_frequency,16,BLACK);
tft.setCursor(pos_x_frequency,pos_y_frequency);
tft.setTextSize(2);
tft.setTextColor(WHITE);
tft.print("DD4WH SDR");
tft.setCursor(pos_x_time,pos_y_time);
tft.setTextSize(1);
tft.setTextColor(WHITE);
tft.print (ver);
delay (2000);
tft.fillRect(pos_x_frequency,pos_y_frequency, 160-pos_x_frequency,16,BLACK); // erase for frequency display
tft.fillRect(pos_x_time, pos_y_time, 80, 8, BLACK); // erase for time display
audioShield.eqSelect (2); // Tone Control
audioShield.eqBands (bass, treble); // (float bass, float treble) in % -100 to +100
audioShield.inputSelect(AUDIO_INPUT_LINEIN); // RX mode uses line ins
setup_gain();
setup_mode(bands[band].mode);
setup_RX(bands[band].mode, bands[band].bandwidthU, bands[band].bandwidthL); // set up the audio chain for reception
FFT_WINDOW_SET (Window_FFT);
FFT_STRING = FFT_WINDOW_STRING (Window_FFT);
audioShield.lineInLevel((float)bands[band].RFgain/10, (float)bands[band].RFgain/10);
if (!(SD.begin(10))) {
// stop here, but print a message repetitively
while (1) {
Serial.println("Unable to access the SD card");
delay(500);
}
}
si5351.init(SI5351_CRYSTAL_LOAD_10PF, 27000000);
// si5351.drive_strength(SI5351_CLK0, SI5351_CLK_DRIVE_8MA); // does this work ???
si5351.set_correction(calibration_constant);
si5351.set_pll(SI5351_PLL_FIXED, SI5351_PLLA);
setfreq();
delay(3);
sinewave();
} // end void SETUP
const char* filename (int pointer) {
String NAME = "REC"+String(pointer)+".RAW";
char str[15];
NAME.toCharArray(str, sizeof(NAME));
return str;
}
void controls() {
// print time every second
if (ms_100.check() == 1) { displayClock();
AudioProcessorUsageMaxReset();
AudioMemoryUsageMaxReset();}
bouncer1.update();
bouncer2.update();
bouncer3.update();
bouncer4.update();
bouncer5.update();
if ( bouncer1.fallingEdge()) {
Serial.print("Play Button Press");
if (Rec_Mode == 1) stopRecording();
if (Rec_Mode == 0){
showtrack();
Rec_Mode = 2;
startPlaying();
}
}
if ( bouncer2.fallingEdge()) {
Serial.print("Stop Button Press");
if (Rec_Mode == 1) stopRecording();
if (Rec_Mode == 2) stopPlaying();
Rec_Mode = 0;
Switch_to_SDR();
Menu = TUNING; Menu2 = TUNING;
RECORDFLAG = 0;
}
if ( bouncer3.fallingEdge()) {
Serial.print("Record Button Press");
if (Rec_Mode == 2) {
stopPlaying();
Switch_to_SDR();
}
if (Rec_Mode == 0) startRecording();
}
if ( bouncer4.fallingEdge()) {
Serial.print("previous track Button Press ");
prevtrack();
}
if ( bouncer5.fallingEdge()) {
Serial.print("next track Button Press");
nexttrack();
}
// If we're playing or recording, carry on...
if (Rec_Mode == 1) {
continueRecording();
}
if (Rec_Mode == 2) {
continuePlaying();
}
}
void startRecording() {
Serial.print("startRecording");
/* if (SD.exists("RECORD.RAW")) {
// The SD library writes new data to the end of the
// file, so to start a new recording, the old file
// must be deleted before new data is written.
SD.remove("RECORD.RAW");
} */
// recfile = recfile + 1;
String NAME = "REC"+String(playfile)+".RAW";
char fi[15];
NAME.toCharArray(fi, sizeof(NAME));
if (SD.exists(fi)) {
// The SD library writes new data to the end of the
// file, so to start a new recording, the old file
// must be deleted before new data is written.
SD.remove(fi);
}
frec = SD.open(fi, FILE_WRITE);
if (frec) {
recorder.begin();
Rec_Mode = 1;
tft.fillRect(0, 104, 160, 10, ST7735_RED); //ST7735_BLACK);
tft.setCursor(0, 105);
tft.print (" Recording !");
showtrack();
}
}
void continueRecording() {
if (recorder.available() >= 2) {
byte buffer[512];
// Fetch 2 blocks from the audio library and copy
// into a 512 byte buffer. The Arduino SD library
// is most efficient when full 512 byte sector size
// writes are used.
memcpy(buffer, recorder.readBuffer(), 256);
recorder.freeBuffer();
memcpy(buffer+256, recorder.readBuffer(), 256);
recorder.freeBuffer();
// write all 512 bytes to the SD card
// elapsedMicros usec = 0;
frec.write(buffer, 512);
// Uncomment these lines to see how long SD writes
// are taking. A pair of audio blocks arrives every
// 5802 microseconds, so hopefully most of the writes
// take well under 5802 us. Some will take more, as
// the SD library also must write to the FAT tables
// and the SD card controller manages media erase and
// wear leveling. The recorder object can buffer
// approximately 301700 us of audio, to allow time
// for occasional high SD card latency, as long as
// the average write time is under 5802 us.
// if (one_sec.check() == 1) {
// Serial.print("SD write, us=");
// }
// Serial.println(usec);
}
}
void stopRecording() {
Serial.print("stopRecording");
recorder.end();
if (Rec_Mode == 1) {
while (recorder.available() > 0) {
frec.write((byte*)recorder.readBuffer(), 256);
recorder.freeBuffer();
}
frec.close();
// playfile = recfile;
}
Rec_Mode = 0;
clearname();
tft.print (" Recording stopped!");
}
void startPlaying() {
String NAME = "REC"+String(playfile)+".RAW";
char fi[15];
NAME.toCharArray(fi, sizeof(NAME));
Serial.print("startPlaying ");
// const char* fi = filename(playfile);
// const char* fi = "REC13.RAW";
// Serial.print ("File: ");Serial.print(playfile);
Serial.println ("Playfile: "); Serial.print (playfile);
Serial.println ("Name: "); Serial.print (filename(playfile));
delay(100);
Switch_to_MP3();
// playSd.play(filename(playfile));
// playSd.play("REC13.RAW");
playSd.play(fi);
Rec_Mode = 2;
tft.fillRect(0, 104, 160, 10, ST7735_GREEN); //ST7735_BLACK);
tft.setCursor(0, 105);
tft.setTextColor(ST7735_BLACK);
tft.print (" Playing !");
tft.setTextColor(ST7735_WHITE);
showtrack();
}
void continuePlaying() {
if (!playSd.isPlaying()) {
playSd.stop();
Rec_Mode = 0;
clearname();
tft.print("End of recording");
}
}
void stopPlaying() {
Serial.print("stopPlaying");
if (Rec_Mode == 2) playSd.stop();
Rec_Mode = 0;
clearname();
tft.print (" Playing stopped");
}
void prevtrack() {
playfile = playfile - 1;
if (playfile < 0) playfile = 0;
if (playfile > 255) playfile = 255;
showtrack();
if (Rec_Mode == 2) {
stopPlaying(); // old track
startPlaying();// new track = old track -1
}
// if (Rec_Mode == 0) startPlaying();
}
void nexttrack() {
playfile = playfile + 1;
if (playfile < 0) playfile = 0;
if (playfile > 255) playfile = 255;
showtrack();
if (Rec_Mode == 2) {
stopPlaying();
startPlaying();
}
}
void showtrack() {
if (Rec_Mode == 1) {
tft.fillRect(pos_x_menu, pos_y_menu - 14, 60, 9, ST7735_RED); // REC
tft.setTextColor(WHITE);
}
if (Rec_Mode == 2) {
tft.fillRect(pos_x_menu, pos_y_menu - 14, 60, 9, ST7735_GREEN); // PLAY
tft.setTextColor(BLACK);
}
if (Rec_Mode == 0) {
tft.fillRect(pos_x_menu, pos_y_menu - 14, 60, 9, BLACK); // STOP
tft.setTextColor(WHITE);
}
tft.setCursor(pos_x_menu, pos_y_menu - 14);
tft.print("Track: "); tft.print(playfile);
// hier printen den track
Serial.print(playfile);
Serial.print(" ");
// Serial.print(recfile); Serial.print(" ");
}
void Switch_to_MP3() {
// set gain values of SDR chain to 0
audioShield.eqBands (0.7, 0.0); // (float bass, float treble) in % -100 to +100
audio_in_I.gain(0, 0); // take values from EEPROM
audio_in_I.gain(1, 0); // take values from EEPROM
audio_in_I.gain(2,0); //not used
audio_in_I.gain(3,0); //not used
audio_in_Q.gain(0, 0); // always 1, only I gain is calibrated
audio_in_Q.gain(1, 0); // take values from EEPROM
audio_in_Q.gain(2,0); //not used
audio_in_Q.gain(3,0); //not used
/* add_substract_I.gain(0, 0);
add_substract_I.gain(1,0); // substract
add_substract_Q.gain(0,0);
add_substract_Q.gain(0,0); // add
*/
// set audio chain to MP3/M4A
mixleft.gain(1, 0); //AAC on
mixright.gain(1,0);
mixleft.gain(0,0); // MP3 on
mixright.gain(0,0);