-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathExtIOFunctions.cpp
1541 lines (1233 loc) · 57.3 KB
/
ExtIOFunctions.cpp
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
/*
Written by Andrus Aaslaid, ES1UVB
andrus.aaslaid(6)gmail.com
http://uvb-76.net
This source code is licensed as Creative Commons Attribution-ShareAlike
(CC BY-SA).
hwlo
From http://creativecommons.org:
This license lets others remix, tweak, and build upon your work even for commercial purposes, as long as they
credit you and license their new creations under the identical terms. This license is often compared to
“copyleft” free and open source software licenses. All new works based on yours will carry the same license,
so any derivatives will also allow commercial use. This is the license used by Wikipedia, and is recommended
for materials that would benefit from incorporating content from Wikipedia and similarly licensed projects.
This DLL provides an empty core for implementing hardware support functionality
for the Winrad Software Defined Radio (SDR) application from Alberto Di Bene (http://www.weaksignals.com/)
and its offsprings supporting the same ExtIO DLL format, most notably the outstanding HDSDR software (http://hdsdr.de)
As the Winrad source is written on Borland C-Builder environment, there has been very little
information available of how the ExtIO DLL should be implemented on Microsoft Visual Studio 2008
(VC2008) environment and the likes.
This example is filling this gap, providing the empty core what can be compiled as appropriate
DLL working for HDSDR
Note, that Winrad and HDSDR are sometimes picky about the DLL filename. The ExtIO_blaah.dll for example,
works, while ExtIODll.dll does not. I havent been digging into depths of that. It is just that if your
custom DLL refuses to be recognized by application for no apparent reason, trying to change the DLL filename
may be a good idea.
To have the DLL built with certain name can be achieved changing the LIBRARY directive inside ExtIODll.def
Revision History:
30.05.2011 - Initial
29.08.2011 - Added Graphical interface and console window to example
23.04.2012 - Removed all extra stuff to strip it down to pure MFC GUI example
24.04.2012 - Cleaned up for public release
08.09.2012 - Replaced About information credits about Winrad according to what LC from hdsdr.de
suggested
- Started to implement libusb functionality
16.09.2012 - libusb data transfer at 192kHz works!! Was "only" one week steady head-banging to
figure out
how to correctly feed data with extio callback ..
18.09.2012 - Switched over to 230kHz family of sample rates
- Created logic for LO drag-alone when frequency is approaching LO bounds
v1.21 27.09.2012 - Added libusb version check on startup at InitHW()
- Added libusb driver and dll version info to DLL screen
v1.22 05.10.2012 - Fixed WaitForSingleObject() issue what caused 100% utilization for one CPU core
(ID_ JBJ01) (Thanx to Mario from hdsdr.de!)
- Slightly restructured thread invoking
- Changed default transparency setting to 90% opaque
v1.23 05.10.2012 - Fixed ugly bug what left hardware LO unchanged when the displayed LO was
changed due required boundaries change
==== The chase for interrupted audio on some platforms (regardless of speed or number of cores) ====
v1.24 08.10.2012 - Improved/more precise ExtIOCallbackTask() timing
- Some of the waiting time excluded from loops
v1.30 28.12.2012 - LO frequency drag-along stripped, since it created more problems than it solved
- Preliminary panadapter support (faulty, zooming does not work etc. but works to some extent)
v1.35 04.01.2013 - Test version - Lots of stuff fixed in panadapter. Does not work perfectly, but is more or less usable.
- Fixes for tuning sync and A/B frequeny switching and storing
v1.36 05.07.2013 - Preliminary TX8M support
v1.37 17.11.2013 - Added SetRegistryKey("Satrian") in InitInstance() to maintain registry access compatibility
with UAC
- ExtIO DLL is now a default data method (was audio interfaces earlier)
- ExtIORegistryUpdateTask() now writes all changes down after every second and checks update flag in 100ms
(was 10sec and 1sec)
- ExtIODialog stuff modified to use HWType field, not fetching value from registry.
- Debug console initialization and removal moved to Init/ExitInstance() code
v1.38 21.11.2013 - Re-layouted GUI to acommodate RF gain control controls
- LibUSB-win32 version checking is now relaxed to 1.2.2.0, since we can live with this and Atmel FLIP utility is
reverting us to old version. Not nice, but could still be used, so lets not complain.
- Replaced all _beginthread stuff with AfxBeginThread() to be more MCF compatible
- Panadapter speed slider made to work
- OnTimer() disables scroll timer temporarily to avoid any possibility for recursion (although there probably wasnt any)
- UI Controls conditional enabling and disabling logic cleaned up.
- Included memwatch 2.71 to the build from http://www.linkdata.se/sourcecode/memwatch/
Old, but functional, since our dynamic memory allocation is using ANSI memory functions anyway.
- Fixed task scheduling issues related to panadapter and ExtIODataTask()
- Eliminated ExtIOCallbackTask() associated double buffering. HDSDR does not block on data callback
and therefore callback is possible to initiate straight from ExtIODataTask()
v1.40 5.12.2013 - UI converted to tabbed dialog (Although only ExtIO tab i simplemented)
- Dialog initialization and cleanup revisited and (most of the) leaks fixed
To Do:
- Make diversity mode work so that both channels could be tweaked, so will get 2x360deg span
* Diversity and gain selection for ExtIO mode
- Sample rate switching for ExtIO mode
x Cache size adjustment control for used form
- A/B/HDSDR channel selection with LO and freq feedback through callback
* Synchronous tuning
* Last freq etc. parameters update for registry
* LO frequency following when tuning reaches the border
* Make Extio DLL as data source a default selection
- Manual gain control dialog implementation (Add firmware command as well)(for older HDSDR versions with no gain slider built-in)
- Rework registry updates, so the local stub could be used when UAC is enabled!
- Fix frequency storing in registry, so each mode will store its own frequency value correctly (A, A+pan = same; B, b+pan=sam etc., TX8m etc.)
* Do something about the fact that FLIP installs 1.2.2.0 libusb driver instead of 1.2.2.6 and ExtIO complains. Are we actually compatible with
1.2.2.0 as well?
- Create twodimensional/class array of enabled/disabled items, frequencys etc., indexed by mode, so we know where the stuff is stored!
* For UI, put all the controls enabling and disabling logic onto ExtIODialog::EnableDisableControls()
- At startup, use single place to read all globals from registry (ExtIODll::InitInstance()? InitHW()?), then use values to
set states at InitDialog() etc. after that.
* replace all _beginthread stuff with AfxBeginThread()
* Fix bug where closing the ExtIO dialog while panadapter is open (what also causes panadapter to close erratically!)
and then re-opening the dialog and panadapter will crash the dll and extio
- clean up thread and other function declarations to .h files
- panadapter shows some banding at some resolutions.
* panadapter speed slider not working
- panadapter speed slider last value not stored in registry
* panadapter speed slider is working backwards
- ExtIO button has to be pressed twice to get the Extio UI showing
* ShowDebug button seems broken (not always in sync with registry)
- See if something can be done about sound breakup when phase is changed (not to reset entire buffer or something..)
- Put globals in separate .cpp and .h file
- Deal with startup error recovery if radio is not connected
- Try to do something no HDSDR restart would be needed if radio disconnected and re-connected.
? Could the scrolltimer variable modification at OnHscroll() cause fatal race condition when OnTimer() tries to re-enable timer?
- Adjust gain for Panadapter dependent on width, so the intensity of the picture stays the same.
- Make the Panadapter intensity slider default less than maximum and reduce gain, so the intensity could also be turned down, if needed.
* Panadapter takes too much resource from CPU
- See if there is a better way dealing with memwatch output redirection to _cprintf then just #ifndef ENABLEMWFILEWRITES
* AGC setting is not saved in registry
* RF gain sliders not saved in registry
- RF gain and AGC functions are not working if extio is used in serial mode. (rfga, rfgb, agc commans not initiated)
To Do v2.x:
- Full rework for new HDSDR ExtioDLL example from LC
- Update google code with new/up to date DLL example code from LC (maintain two branches -- legacy and new)
- Implement firmware update function through ExtIO dll, including automated FLIP and Atmel driver installation.
Have Exe what allows invoking this feature from external app as well for non-hdsdr users.
- Fix panadapter
- Network support! + UDP discovery + UDP IP address renewal for radio (for peer-to-peer with direct cable connections)
- Port to libusbK for Win8 compatibility (http://code.google.com/p/usb-travis/downloads/list)
- See if void CMainDialog::ShowTabDlg(int tabSel) handles multiple monitors right (do we have screen coordinates really?)
*/
#include "stdafx.h"
#include "ExtIODll.h"
//#include "ExtIODialog.h"
#include "MainDialog.h"
#include "ExtIOFunctions.h"
#include "PanadapterDialog.h"
#include "libusb\lusb0_usb.h"
//serial.h is included from ExtIOFunctions.h
#include <conio.h> // gives _cprintf()
#include <process.h> // gives threading
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
UINT ExtIODataTask(LPVOID dummy);
UINT ExtIOCallbackTask(LPVOID dummy);
extern HANDLE sleepevent;
/*
Some notes:
- The functions are exported as simple functions, no being part of any class. This is caused by the fact that we
do not need the class functionality, as well as the fact that this DLL will be used by Borland C-builder
environment, what sets the certain requirements for the DLL.
See http://rvelthuis.de/articles/articles-cppobjs.html and http://bytes.com/topic/c/answers/731703-use-visual-c-dll-c-builder
for some reference information, why we can not export this as a class.
- The interface is assuming 8-bit characters, so be sure that you have Configuration Properties->General->Character Set set to "Not Set"
*/
#ifndef SERIAL_NO_OVERLAPPED
#error SERIAL_NO_OVERLAPPED has to be defined
#endif
//CExtIODialog* m_pmodeless;
extern CPanadapterDialog* m_pmodelessPanadapter;
CMainDialog* m_pmodeless;
//bool hasconsole=false;
unsigned long lo_freq=4624000L;
unsigned long tune_freq=0;
unsigned char modulation='U';
unsigned long iflimit_high=-1;
unsigned long iflimit_low=0;
// our serial port class. Have to access it from many places, so keep global.
CSerial serial;
volatile bool serinit_ok=false;
volatile bool libusb_ok=false;
int HWType;
volatile bool do_datatask = false; // indicator that the datatask must be run
extern volatile bool do_callbacktask;
volatile bool datatask_running = false; // indicator that we are actually inside the data task
volatile bool datatask_done = false; // will be set at exit to true to indicate that task has left a building
volatile bool lo_changed = false;
extern volatile bool channelmode_changed;
volatile bool do_callback105=false; // indicate, if datatask should call callback 105
volatile bool do_callback101=false; // indicate, if datatask should call callback 101
volatile bool update_registry=false;
extern volatile bool fifo_loaded;
volatile bool globalshutdown = false; // indicate that all threads must terminate
volatile int threadcount=0; // number of running threads;
CRITICAL_SECTION CriticalSection; // global thread locking object
int ChannelMode; // indicates, which channel mode is selected by extio dialog
int SyncTuning;
int SyncGain;
unsigned long lasttune_freqA=-1; // init so, that HDSDR would update immediately on startup
unsigned long lasttune_freqB=-1;
unsigned long lasttune_freqC=-1;
extern volatile bool update_lo;
unsigned long lastlo_freqA=-1;
unsigned long lastlo_freqB=-1;
unsigned long lastlo_freqC=-1;
extern int Transparency;
long IQSampleRate;
int IFGainA, IFGainB;
int RFGainA, RFGainB;
int AGC;
int PhaseCoarse, PhaseFine;
int DebugConsole;
extern volatile bool update_phaseA;
extern volatile bool update_IFgain;
extern volatile bool update_RFgain;
usb_dev_handle *dev = NULL; // device handle to be used by libusb
const struct usb_version* libver = NULL; // libusb version information
//char tmp[128];
CWnd* MainWindow;
usb_dev_handle *open_dev(void)
{
struct usb_bus *bus;
struct usb_device *dev;
for (bus = usb_get_busses(); bus; bus = bus->next)
{
for (dev = bus->devices; dev; dev = dev->next)
{
if (dev->descriptor.idVendor == SDR_VID
&& dev->descriptor.idProduct == SDR_PID)
{
return usb_open(dev);
}
}
}
return NULL;
}
/*
Monitors the LO and Tuning frequency for channels and if changed, writes new value to registry.
NB! Only to be used for data what changes rapidly (sliders, frequency etc.) and can not be written therefore in real-time.
All checkboxes, lists etc. have to be written down at the UI task immediately.
*/
UINT ExtIORegistryUpdateTask(LPVOID dummy)
{
static long lasttuna=-1, lasttunb=-1, lasttunc=-1, lastloa=-1, lastlob=-1, lastloc=-1;
static long lasttransparency=-1, lastifgaina=-1, lastifgainb=-1, lastrfgaina=-1, lastrfgainb=-1;
static long lastcoarse=-1, lastfine=-1;
static int lasthwtype=-1;
int i;
// Wait for fifo to be filled with data before returning to program
while(!globalshutdown)
{
for (i=0; i<10; i++) //check every second for variable updates (10x100ms)
{
if (update_registry)
{
update_registry=false; // should be in critical section really, but works without as well
break;
}
WaitForSingleObject(sleepevent, 100); // do it only after every 100ms
}
if (lastlo_freqA != lastloa)
{
AfxGetApp()->WriteProfileInt(_T("Config"), _T("LastLO_A"), lastlo_freqA);
lastloa=lastlo_freqA;
}
if (lastlo_freqB != lastlob)
{
AfxGetApp()->WriteProfileInt(_T("Config"), _T("LastLO_B"), lastlo_freqB);
lastlob=lastlo_freqB;
}
if (lastlo_freqC != lastloc)
{
AfxGetApp()->WriteProfileInt(_T("Config"), _T("LastLO_C"), lastlo_freqC);
lastloc=lastlo_freqC;
}
if (lasttune_freqA != lasttuna)
{
AfxGetApp()->WriteProfileInt(_T("Config"), _T("LastTune_A"), lasttune_freqA);
lasttuna=lasttune_freqA;
}
if (lasttune_freqB != lasttunb)
{
AfxGetApp()->WriteProfileInt(_T("Config"), _T("LastTune_B"), lasttune_freqB);
lasttunb=lasttune_freqB;
}
if (lasttune_freqC != lasttunc)
{
AfxGetApp()->WriteProfileInt(_T("Config"), _T("LastTune_C"), lasttune_freqC);
lasttunc=lasttune_freqC;
}
if (Transparency != lasttransparency)
{
AfxGetApp()->WriteProfileInt(_T("Config"), _T("Transparency"), Transparency);
lasttransparency=Transparency;
}
if (PhaseCoarse != lastcoarse)
{
AfxGetApp()->WriteProfileInt(_T("Config"), _T("PhaseCoarse"), PhaseCoarse);
lastcoarse=PhaseCoarse;
}
if (PhaseFine != lastfine)
{
AfxGetApp()->WriteProfileInt(_T("Config"), _T("PhaseFine"), PhaseFine);
lastfine=PhaseFine;
}
if (IFGainA != lastifgaina)
{
AfxGetApp()->WriteProfileInt(_T("Config"), _T("IFGainA"), IFGainA);
lastifgaina=IFGainA;
}
if (IFGainB != lastifgainb)
{
AfxGetApp()->WriteProfileInt(_T("Config"), _T("IFGainB"), IFGainB);
lastifgainb=IFGainB;
}
if (RFGainA != lastrfgaina)
{
AfxGetApp()->WriteProfileInt(_T("Config"), _T("RFGainA"), RFGainA);
lastrfgaina=RFGainA;
}
if (RFGainB != lastrfgainb)
{
AfxGetApp()->WriteProfileInt(_T("Config"), _T("RFGainB"), RFGainB);
lastrfgainb=RFGainB;
}
}
return 1;
}
/*
This entry is the first called by Winrad at startup time, and it is used both to tell to the DLL that it is
time to initialize the hardware, and to get back a descriptive name and model (or Serial Number) of the HW,
together with a type code.
Parameters :
name - descriptive name of the hardware. Preferably not longer than about 16 characters, as it will be used in a Winrad menu.
model - model code of the hardware, or its Serial Number. Keep also this field not too long, for the same reason of the previous one.
type - this is an index code that Winrad uses to identify the hardware type supported by the DLL.
Please use one the following values :
3 the hardware does its own digitization and the audio data are returned to Winrad via the callback device. Data must be in 16-bit (short) format, little endian.
4 The audio data are returned via the sound card managed by Winrad. The external hardware just controls the LO, and possibly a preselector, under DLL control.
5 the hardware does its own digitization and the audio data are returned to Winrad via the callback device. Data are in 24-bit integer format, little endian.
6 the hardware does its own digitization and the audio data are returned to Winrad via the callback device. Data are in 32-bit integer format, little endian.
7 the hardware does its own digitization and the audio data are returned to Winrad via the callback device. Data are in 32-bit float format, little endian.
Please ask me ([email protected]) for the assignment of an index code for cases different from the above.
Return value :
true - everything went well, the HW did initialize, and the return parameters have been filled.
false - the HW did not initialize (error, or powered off, or other reasons).
*/
extern "C" bool __stdcall InitHW(char *name, char *model, int& type)
{
//static bool first = true;
char errstring[128];
unsigned long long libvernum;
DebugConsole = AfxGetApp()->GetProfileInt(_T("Config"), _T("DebugConsole"), 0);
Transparency = AfxGetApp()->GetProfileInt(_T("Config"), _T("Transparency"), DEFAULTTRANSPARENCY);
//consoletransparency=Transparency;
//if (consoletransparency == -1)
// consoletransparency = DEFAULTTRANSPARENCY;
//--------------
// Create console. This is convenient for _cprintf() debugging, but as we will
// set up this as a transparent window, it may also be of use for other things.
//--------------
/*
Moved to InitInstance()
if (DebugConsole)
{
if (!AllocConsole())
{
hasconsole=false;
AfxMessageBox("Failed to create the console!", MB_ICONEXCLAMATION);
}
else
{
HWND hWnd;
hasconsole=true;
HANDLE nConsole = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleTextAttribute(nConsole, 0x0002|0x0008); //green|white to get the bright color
SetConsoleTitle("ExtIO DLL Console");
hWnd=GetConsoleWindow();
SetWindowLong(hWnd, GWL_EXSTYLE, ::GetWindowLong(hWnd, GWL_EXSTYLE) | WS_EX_LAYERED); // add layered attribute
SetLayeredWindowAttributes(hWnd, 0, 255 * consoletransparency / 100, LWA_ALPHA); // set transparency in percents
//Have to disable close button, as this will kill the application instance with no questions asked!
//Note, that application is still terminated when the consle is closed from taskbar.
DeleteMenu(GetSystemMenu(hWnd, false), SC_CLOSE, MF_BYCOMMAND);
_cprintf("InitHW(): Console initialized\n");
}
}
*/
InitializeCriticalSectionAndSpinCount(&CriticalSection, 0x00000400);
m_pmodeless=NULL; // reset GUI so we will be able to track the state
HWType = AfxGetApp()->GetProfileInt(_T("Config"), _T("HardwareType"), 3);
// get channel mode and tuning sync setting
ChannelMode=AfxGetApp()->GetProfileInt(_T("Config"), _T("ChannelMode"), CHMODE_A);
SyncTuning=AfxGetApp()->GetProfileInt(_T("Config"), _T("SyncTuning"), 0);
SyncGain=AfxGetApp()->GetProfileInt(_T("Config"), _T("SyncGain"), 0);
IFGainA=AfxGetApp()->GetProfileInt(_T("Config"), _T("IFGainA"), 7);
IFGainB=AfxGetApp()->GetProfileInt(_T("Config"), _T("IFGainB"), 7);
RFGainA=AfxGetApp()->GetProfileInt(_T("Config"), _T("RFGainA"), 7);
RFGainB=AfxGetApp()->GetProfileInt(_T("Config"), _T("RFGainB"), 7);
PhaseCoarse=AfxGetApp()->GetProfileInt(_T("Config"), _T("PhaseCoarse"), 0);
PhaseFine=AfxGetApp()->GetProfileInt(_T("Config"), _T("PhaseFine"), 0);
AGC=AfxGetApp()->GetProfileInt(_T("Config"), _T("AGC"), 1);
/*
// for diversity modes, force tune syncing!
if ((ChannelMode > 1)&&(ChannelMode < 5))
{
IQSampleRate = IQSAMPLERATE_DIVERSITY;
SyncTuning=1;
}
else if ((ChannelMode <=1)||(ChannelMode == 7))
{
IQSampleRate = IQSAMPLERATE_FULL;
}
else
{
IQSampleRate = IQSAMPLERATE_PANADAPTER;
}
*/
// for all other modes than ExtIO DLL we are supporting only standard mode and diversity mode, which is the same as A-B
if ((HWType != 3) && (!(ChannelMode == CHMODE_A)||(ChannelMode == CHMODE_AMB)))
ChannelMode=CHMODE_A;
_cprintf("ChannelMode = %d\n", ChannelMode);
if (HWType != 3)
{
IQSampleRate = IQSAMPLERATE_AUDIO;
}
else
{
switch(ChannelMode)
{
case CHMODE_A:
case CHMODE_B:
IQSampleRate = IQSAMPLERATE_FULL;
break;
case CHMODE_APB:
case CHMODE_AMB:
case CHMODE_BMA:
IQSampleRate = IQSAMPLERATE_DIVERSITY;
SyncTuning=1;
break;
case CHMODE_ABPAN:
case CHMODE_BAPAN:
IQSampleRate = IQSAMPLERATE_PANADAPTER;
break;
case CHMODE_IABQ:
default:
IQSampleRate = IQSAMPLERATE_FULL;
break;
}
}
// There is not really a good way for fetching the LO and Tune frequency from HDSDR when noone has actually touched
// the frequency controls. Therefore, we are mirroring those ourselves and init the HDSDR to appropriate values ...
lastlo_freqA=AfxGetApp()->GetProfileInt(_T("Config"), _T("LastLO_A"), 0);
lastlo_freqB=AfxGetApp()->GetProfileInt(_T("Config"), _T("LastLO_B"), 0);
lastlo_freqC=AfxGetApp()->GetProfileInt(_T("Config"), _T("LastLO_C"), 0);
lasttune_freqA=AfxGetApp()->GetProfileInt(_T("Config"), _T("LastTune_A"), 0);
lasttune_freqB=AfxGetApp()->GetProfileInt(_T("Config"), _T("LastTune_B"), 0);
lasttune_freqC=AfxGetApp()->GetProfileInt(_T("Config"), _T("LastTune_C"), 0);
switch(ChannelMode)
{
case CHMODE_B:
case CHMODE_BMA:
case CHMODE_BAPAN:
lo_freq=lastlo_freqB;
tune_freq=lasttune_freqB;
break;
case CHMODE_IABQ:
lo_freq=lastlo_freqC;
tune_freq=lasttune_freqC;
break;
default:
lo_freq=lastlo_freqA;
tune_freq=lasttune_freqA;
break;
}
AfxBeginThread((AFX_THREADPROC)ExtIORegistryUpdateTask, NULL);
if ((HWType == -1)||(HWType < 3) || (HWType > 7))
HWType = 4; //4 ==> data returned via the sound card
type = HWType;
_cprintf("HWType = %d\n", HWType);
if (name != NULL)
strcpy(name, "SDR MK1.5 Andrus"); // change with the name of your HW
if (model != NULL)
strcpy(model, "SDR MK1.5 Andrus"); // change with the model of your HW
if (HWType == 3)
{
_cprintf("Initializing libusb\n");
////////////
// init libusb
usb_init(); // initialize the library
libver=usb_get_version();
if (libver)
{
// for easy comparision, lets build a number out of versions! xxx.xxx.xxx.xxx
libvernum=(libver->dll.major*1000000000)+(libver->dll.minor*1000000)+(libver->dll.micro*1000)+libver->dll.nano;
if (libvernum < ((LIB_MIN_MAJOR*1000000000)+(LIB_MIN_MINOR*1000000)+(LIB_MIN_MICRO*1000)+LIB_MIN_NANO))
{
sprintf_s(errstring, 128, "Incompatible LibUSB-win32 DLL: v%d.%d.%d.%d (Minimum Required: v%d.%d.%d.%d)",
libver->dll.major, libver->dll.minor, libver->dll.micro, libver->dll.nano,
LIB_MIN_MAJOR, LIB_MIN_MINOR, LIB_MIN_MICRO, LIB_MIN_NANO);
AfxMessageBox(errstring, MB_ICONEXCLAMATION);
}
libvernum=(libver->driver.major*1000000000)+(libver->driver.minor*1000000)+(libver->driver.micro*1000)+libver->driver.nano;
if (libvernum < ((LIB_MIN_MAJOR*1000000000)+(LIB_MIN_MINOR*1000000)+(LIB_MIN_MICRO*1000)+LIB_MIN_NANO))
{
sprintf_s(errstring, 128, "Incompatible LibUSB-win32 Driver: v%d.%d.%d.%d (Minimum Required: v%d.%d.%d.%d)",
libver->driver.major, libver->driver.minor, libver->driver.micro, libver->driver.nano,
LIB_MIN_MAJOR, LIB_MIN_MINOR, LIB_MIN_MICRO, LIB_MIN_NANO);
AfxMessageBox(errstring, MB_ICONEXCLAMATION);
}
}
else
{
AfxMessageBox("Unable to Fetch LibUSB-win32 Version Information!", MB_ICONEXCLAMATION);
}
usb_find_busses(); // find all busses
usb_find_devices(); // find all connected devices
libusb_ok=true;
if (!(dev = open_dev()))
{
sprintf_s(errstring, 128, "Error opening device: %s\n", usb_strerror());
AfxMessageBox(errstring, MB_ICONEXCLAMATION);
_cprintf(errstring);
libusb_ok=false;
}
else if (usb_set_configuration(dev, SDR_CONFIG) < 0)
{
sprintf_s(errstring, 128, "Error setting config #%d: %s\n", SDR_CONFIG, usb_strerror());
AfxMessageBox(errstring, MB_ICONEXCLAMATION);
_cprintf(errstring);
usb_close(dev);
libusb_ok=false;
}
else if (usb_claim_interface(dev, SDR_BULKIF) < 0) // check, if we can claim the bulk interface from radio?
{
sprintf_s(errstring, 128, "Error claiming interface #%d:\n%s\n", SDR_BULKIF, usb_strerror());
AfxMessageBox(errstring, MB_ICONEXCLAMATION);
_cprintf(errstring);
usb_close(dev);
libusb_ok=false;
}
}
return true;
}
/*
This entry is called by Winrad each time the user specifies that Winrad should receive its audio data input through
the hardware managed by this DLL, or, if still using the sound card for this, that the DLL must activate the control
of the external hardware. It can be used by the DLL itself for delayed init tasks, like, e.g., the display of its own
GUI, if the DLL has one.
It has no parameters.
Return value :
true - everything went well.
false - some error occurred, the external HW cannot be controlled by the DLL code.
*/
extern "C" bool __stdcall OpenHW(void)
{
//.... display here the DLL panel ,if any....
//.....if no graphical interface, delete the following statement
//::SetWindowPos(F->handle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
// in the above statement, F->handle is the window handle of the panel displayed
// by the DLL, if such a panel exists
_cprintf("OpenHW() called\n");
return true;
}
/*
usb_dev_handle *open_dev(void)
{
struct usb_bus *bus;
struct usb_device *dev;
for (bus = usb_get_busses(); bus; bus = bus->next)
{
for (dev = bus->devices; dev; dev = dev->next)
{
if (dev->descriptor.idVendor == SDR_VID
&& dev->descriptor.idProduct == SDR_PID)
{
return usb_open(dev);
}
}
}
return NULL;
}
*/
/*
This entry is called by Winrad each time the user presses the Start button on the Winrad main screen, after having
previously specified that the DLL is in control of the external hardware.
Parameters :
freq - an integer specifying the frequency the HW should be set to, expressed in Hz.
Return value :
An integer specifying how many I/Q pairs are returned by the DLL each time the callback function is invoked (see later).
This information is used of course only when the input data are not coming from the sound card, but through the callback device.
If the number is negative, that means that an error has occurred, Winrad interrupts the starting process and returns to the idle status.
The number of I/Q pairs must be at least 512, or an integer multiple of that value.
*/
extern "C" int __stdcall StartHW(long freq)
{
char errstring[128];
long lLastError;
char sdrport[16];
char freqstring[32];
unsigned long phaseword;
char modetmp[16];
//if libusb is not OK for some reason (for example, when radio was not connected at startup)
//try once more once here!
if (!libusb_ok)
{
libusb_ok=true;
usb_find_busses(); // find all busses
usb_find_devices(); // find all connected devices
if (!(dev = open_dev()))
{
sprintf_s(errstring, 128, "Error opening device: \n%s\n", usb_strerror());
AfxMessageBox(errstring, MB_ICONEXCLAMATION);
_cprintf(errstring);
libusb_ok=false;
}
else if (usb_set_configuration(dev, SDR_CONFIG) < 0)
{
sprintf_s(errstring, 128, "Error setting config #%d: %s\n", SDR_CONFIG, usb_strerror());
AfxMessageBox(errstring, MB_ICONEXCLAMATION);
_cprintf(errstring);
usb_close(dev);
libusb_ok=false;
}
else if (usb_claim_interface(dev, SDR_BULKIF) < 0) // check, if we can claim the bulk interface from radio?
{
sprintf_s(errstring, 128, "Error claiming interface #%d:\n%s\n", SDR_BULKIF, usb_strerror());
AfxMessageBox(errstring, MB_ICONEXCLAMATION);
_cprintf(errstring);
usb_close(dev);
libusb_ok=false;
}
}
if (HWType == 3) // extio DLL manages sample data, so we are going to bulk USB mode and control the radio using USB control messages!
{
if (!libusb_ok)
{
_cprintf("StartHW(): libusb is not ok!\n");
return -1; // indicate error nd stop whole process
}
do_datatask=true;
do_callbacktask=true;
//libusb_ok=true;
fifo_loaded=false;
//AfxBeginThread((AFX_THREADPROC)ExtIOCallbackTask, NULL);
AfxBeginThread((AFX_THREADPROC)ExtIODataTask, NULL);
// Wait for fifo to be filled with data before returning to program
/*
while(!globalshutdown)
{
WaitForSingleObject(sleepevent, 0); // give away timeslice
if (fifo_loaded==true)
break;
}
*/
}
else
{
// regardless of whenever or not the radio actually has a bulk transfer capable firmware installed,
// try issuing a control message to it to put the radio back to 2x48kHz audio mode should we have been in bulk mode before.
if (libusb_ok)
{
usb_clear_halt(dev, SDREP_IN);
*(unsigned long*)&modetmp[0]=IQSAMPLERATE_AUDIO;
modetmp[4]=16;
usb_control_msg(dev, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_OUT,
LIBUSB_SAMPLEMODE,
0,
SDR_BULKIF,
modetmp, 5, 1000);
usb_control_msg(dev, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_IN,
LIBUSB_MODE,
LIBMODE_OFF,
SDR_BULKIF,
modetmp, 1, 1000);
}
// Open serial port
int nPortNum = AfxGetApp()->GetProfileInt(_T("Config"), _T("ComPort"), -1);
if (nPortNum == -1)
{
sprintf_s(errstring, 128, "No COM Port Selected - Press ExtIO Button");
AfxMessageBox(errstring, MB_ICONEXCLAMATION);
return(-1);
}
sprintf_s(sdrport, 16, "\\\\.\\\\COM%d", nPortNum);
lLastError=serial.Open(sdrport,9*1024,9*1024,false); // non-overlapped. Also, take 9k for input and output queues queue.
if (lLastError != ERROR_SUCCESS)
{
sprintf_s(errstring, 128, "Unable to open port COM%d (err=%ld)", nPortNum, serial.GetLastError());
AfxMessageBox(errstring, MB_ICONEXCLAMATION);
return(-1);
}
// Setup serial port (8N1). For CDC port the baudrate is not really important
lLastError += serial.Setup(CSerial::EBaud256000,CSerial::EData8,CSerial::EParNone,CSerial::EStop1);
lLastError += serial.SetupHandshaking(CSerial::EHandshakeOff);
/*
lLastError += serial.SetMask(CSerial::EEventBreak |
CSerial::EEventCTS |
CSerial::EEventDSR |
CSerial::EEventError |
CSerial::EEventRing |
CSerial::EEventRLSD |
CSerial::EEventRecv);
*/
// Use 'non-blocking' reads, because we don't know how many bytes
// will be received. This is normally the most convenient mode
// (and also the default mode for reading data).
lLastError += serial.SetupReadTimeouts(CSerial::EReadTimeoutNonblocking);
if (lLastError)
{
sprintf_s(errstring, 128, "Unable to configure port COM%d", nPortNum);
AfxMessageBox(errstring, MB_ICONEXCLAMATION);
return(-1);
}
/*
HANDLE hevtOverlapped = ::CreateEvent(0,TRUE,FALSE,0);;
OVERLAPPED ov = {0};
ov.hEvent = hevtOverlapped;
HANDLE hevtStop = ::CreateEvent(0,TRUE,FALSE,_T("Overlapped_Stop_Event_SDR"));
*/
serinit_ok=true;
// sync radio freq with HDSDR displayed freq.
// This is mandatory, as HDSRD calls SetHWLO() before the function here, and as serial is not initialized
// at this point, we will havd LO frequency out of sync for SDR otherwise
if (ChannelMode == CHMODE_AMB)
sprintf_s(freqstring, 32, "diversity 1\n\r");
else
sprintf_s(freqstring, 32, "diversity 0\n\r");
serial.Write(freqstring, strlen(freqstring));
sprintf_s(freqstring, 32, "_fa %ld\n\r", lo_freq);
serial.Write(freqstring, strlen(freqstring));
}
// set or ask datatask to set default values for gain and phase
if (HWType != 3)
{
sprintf_s(freqstring, 32, "_ga %d\n\r", IFGainA);
serial.Write(freqstring, strlen(freqstring));
sprintf_s(freqstring, 32, "_gb %d\n\r", IFGainB);
serial.Write(freqstring, strlen(freqstring));
phaseword=1820; //65535=2pi rad = 360deg; 65535/360*1000
phaseword*=(PhaseCoarse*100)+PhaseFine;
phaseword/=1000;
sprintf_s(freqstring, 32, "_pa %ld\n\r", phaseword);
serial.Write(freqstring, strlen(freqstring));
return(0);
}
else
{
update_IFgain=true;
update_RFgain=true;
update_phaseA=true;
channelmode_changed=true;
lo_changed=true;
return (IQPAIRS); // number of complex elements returned each invocation of the callback routine
}
}
/*
This entry is called by Winrad each time the user presses the Stop button on the Winrad main screen. It can be used by the DLL
for whatever task might be needed in such an occurrence. If the external HW does not provide the audio data, being, e.g.,
just a DDS or some other sort of an oscillator, typically this call is a no-op. The DLL could also use this call to hide its GUI, if any.
If otherwise the external HW sends the audio data via the USB port, or any other hardware port managed by the DLL, when this
entry is called, the HW should be commanded by the DLL to stop sending data.
It has no parameters and no return value.
*/
extern "C" void __stdcall StopHW(void)
{
char modetmp[16];
_cprintf("StopHW() called\n");
if (serinit_ok)
{
serinit_ok=false;
serial.Close();
}
if (datatask_running) // do we have a running data task?
{
_cprintf("StopHW: waiting for data thread to shut down (threadcount=%d)\n", threadcount);
do_datatask=false; // thread shutdown flag for ExtIOIQData task
while (!datatask_done)
{
WaitForSingleObject(sleepevent, 1); // give away timeslice
}
_cprintf("StopHW: data thread finished (threadcount=%d)\n", threadcount);
}
if ((HWType == 3)&&(libusb_ok))
{
// return SDR to audio mode
*(unsigned long*)&modetmp[0]=IQSAMPLERATE_AUDIO;
modetmp[4]=16;
usb_control_msg(dev, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_OUT,
LIBUSB_SAMPLEMODE,
0,
SDR_BULKIF,
modetmp, 5, 1000);
usb_control_msg(dev, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_IN,
LIBUSB_MODE,
LIBMODE_OFF,
SDR_BULKIF,
modetmp, 1, 1000);
}
return;
}
/*
This entry is called by Winrad when the User indicates that the control of the external HW is no longer needed or wanted.
This is done in Winrad by choosing ShowOptions | Select Input then selecting either WAV file or Sound Card. The DLL can use this
information to e.g. shut down its GUI interface, if any, and possibly to put the controlled HW in a idle status.
It has no parameters and no return value.
*/
extern "C" void __stdcall CloseHW(void)
{
// ..... here you can shutdown your graphical interface, if any............
// this is a right place to shut down graphical interface, as HideGUI() does not get called during he exit in some
// reason and therefore is not possible to use for cleanup.
if (m_pmodeless)
{
//does the following actually leak resources?
//we cant send close messages here, because
//app is getting terminated before the message gets processed.
if (m_pmodeless)
{