1 unit uCWKeying;
2 
3 {$mode objfpc}{$H+}
4 
5 interface
6 
7 uses
8   Classes, SysUtils, synaser, synautil, lNet, lNetComponents, Forms;
9 
10 type TKeyType   = (ktCWdaemon, ktWinKeyer);
11 type TKeyStatus = (ksReady, ksBusy);
12 
13 type
14   TCWDevice = class
15     protected
16       fPort      : String;
17       fLastErrNr : Word;
18       fLastErrSt : String;
19       fDevice    : String;
20       fDebugMode : Boolean;
21       fMinSpeed  : Word;
22       fMaxSpeed  : Word;
23       fPortSpeed : dWord;
24     public
25       property Port      : String read fPort write fPort;
26       property Device    : String read fDevice write fDevice;
27       property LastErrNr : Word read fLastErrNr;
28       property LastErrSt : String read fLastErrSt;
29       property DebugMode : Boolean read fDebugMode write fDebugMode;
30       property MinSpeed  : Word read fMinSpeed;
31       property MaxSpeed  : Word read fMaxSpeed;
32       property PortSpeed : dWord read fPortSpeed write fPortSpeed;
33 
34       constructor Create; virtual; abstract;
35 
GetSpeednull36       function GetSpeed  : Word; virtual; abstract;
GetStatusnull37       function GetStatus : TKeyStatus; virtual; abstract;
38 
39       procedure Open; virtual; abstract;
40       procedure Close; virtual; abstract;
41       procedure SetSpeed(speed : Word); virtual; abstract;
42       procedure SendText(text : String); virtual; abstract;
43       procedure StopSending; virtual; abstract;
44       procedure DelLastChar; virtual; abstract;
45       procedure SetMixManSpeed(min,max : Word); virtual; abstract;
46       procedure TuneStart; virtual; abstract;
47       procedure TuneStop; virtual; abstract;
48   end;
49 
50   TCWWinKeyerUSB = class(TCWDevice)
51     private
52       fActive : Boolean;
53       fSpeed  : Word;
54       ser     : TBlockSerial;
55 
56     public
57       constructor Create; override;
58       destructor  Destroy; override;
59 
GetSpeednull60       function GetSpeed  : Word; override;
GetStatusnull61       function GetStatus : TKeyStatus; override;
62 
63       procedure Open; override;
64       procedure Close; override;
65       procedure SetSpeed(speed : Word); override;
66       procedure SendText(text : String); override;
67       procedure StopSending; override;
68       procedure DelLastChar; override;
69       procedure SetMixManSpeed(min,max : Word); override;
70       procedure TuneStart; override;
71       procedure TuneStop; override;
72   end;
73 
74   TCWDaemon = class(TCWDevice)
75     private
76       fActive : Boolean;
77       fSpeed  : Word;
78       udp     : TLUDPComponent;
79     public
80       constructor Create; override;
81       destructor  Destroy; override;
82 
GetSpeednull83       function GetSpeed  : Word; override;
GetStatusnull84       function GetStatus : TKeyStatus; override;
85 
86       procedure Open; override;
87       procedure Close; override;
88       procedure SetSpeed(speed : Word); override;
89       procedure SendText(text : String); override;
90       procedure StopSending; override;
91       procedure DelLastChar; override;
92       procedure SetMixManSpeed(min,max : Word); override;
93       procedure TuneStart; override;
94       procedure TuneStop; override;
95   end;
96 
97   TCWK3NG = class(TCWDevice)
98     private
99       fActive : Boolean;
100       fSpeed  : Word;
101       ser     : TBlockSerial;
102     public
103       constructor Create; override;
104       destructor  Destroy; override;
105 
GetSpeednull106       function GetSpeed  : Word; override;
GetStatusnull107       function GetStatus : TKeyStatus; override;
108 
109       procedure Open; override;
110       procedure Close; override;
111       procedure SetSpeed(speed : Word); override;
112       procedure SendText(text : String); override;
113       procedure StopSending; override;
114       procedure DelLastChar; override;
115       procedure SetMixManSpeed(min,max : Word); override;
116       procedure TuneStart; override;
117       procedure TuneStop; override;
118   end;
119 
120   TCWHamLib = class(TCWDevice)
121     private
122       AllowCW : Boolean;
123       fActive : Boolean;
124       fSpeed  : Word;
125       tcp     : TLTCPComponent;
126       Rmsg    : String;
127       procedure OnReceived(aSocket: TLSocket);
128       procedure OnHamLibConnect(aSocket: TLSocket);
129       procedure OnHamLibError(const msg: AnsiString; aSocket: TLSocket);
130     public
131       constructor Create; override;
132       destructor  Destroy; override;
133 
GetSpeednull134       function GetSpeed  : Word; override;
GetStatusnull135       function GetStatus : TKeyStatus; override;
136 
137       procedure Open; override;
138       procedure Close; override;
139       procedure WaitMorse;
140       procedure SetSpeed(speed : Word); override;
141       procedure SendText(text : String); override;
142       procedure StopSending; override;
143       procedure DelLastChar; override;
144       procedure SetMixManSpeed(min,max : Word); override;
145       procedure TuneStart; override;
146       procedure TuneStop; override;
147   end;
148 
149 
150 implementation
151 
152 uses fTRXControl;
153 
154 constructor TCWWinKeyerUSB.Create;
155 begin
156   fActive       := False;
157   fDebugMode    := False;
158   ser           := TBlockserial.Create;
159   ser.LinuxLock := False;
160   fMinSpeed     := 5;
161   fMaxSpeed     := 60
162 end;
163 
164 procedure TCWWinKeyerUSB.Open;
165 var
166   rec : byte;
167 begin
168   if fActive then Close();
169 
170   if fDebugMode then Writeln('Device: ',fDevice);
171   ser.RaiseExcept := False;
172   ser.Connect(fDevice);
173   ser.Config(1200,8,'N',2,false,false);
174   ser.DTR:=True;
175   ser.RTS:=False;
176   fLastErrNr := ser.LastError;
177   fLastErrSt := ser.LastErrorDesc;
178   if fDebugMode then
179   begin
180     Writeln('Last error nr:  ',fLastErrNr);
181     Writeln('Last error desc:',fLastErrSt)
182   end;
183   if LastErrNr > 0 then
184     exit;
185   ser.SendByte($13);
186   ser.SendByte($13);  //sending null commands
187   ser.SendByte($13);
188   sleep(50);
189   if fDebugMode then Writeln('After sending null command');
190   ser.SendByte(0);
191   ser.SendByte(4);  //send echo command
192   ser.SendByte(20);
193   sleep(50);
194   while ser.CanReadex(10) do
195   begin
196     rec := (ser.recvByte(0))
197   end;
198   if fDebugMode then Writeln('After sending echo command: ',rec);
199   if rec = 20 then
200     fActive := True
201   else begin
202     fLastErrNr := 1000;
203     fLastErrSt := 'WinKeyer USB inicialization failed';
204     exit
205   end;
206   ser.SendByte(0);
207   ser.SendByte(2); //enable communication
208   sleep(50);
209   while ser.CanReadex(10) do
210   begin
211     rec := (ser.recvByte(0))
212   end;
213   if fDebugMode then Writeln('Firmware version: ',rec);
214   fActive := True;
215 
216   SetSpeed(fSpeed)
217 end;
218 
219 procedure TCWWinKeyerUSB.SetSpeed(speed : Word);
220 begin
221   if fDebugMode then Writeln('Speed: ',speed);
222   fSpeed := speed;
223   ser.Flush;
224   ser.SendByte(2);
225   ser.SendByte(speed);
226   sleep(50)
227 end;
228 
GetSpeednull229 function TCWWinKeyerUSB.GetSpeed  : Word;
230 begin
231   Result := fSpeed
232 end;
233 
TCWWinKeyerUSB.GetStatusnull234 function TCWWinKeyerUSB.GetStatus : TKeyStatus;
235 begin
236   Result := ksBusy //not implemented, default value
237 end;
238 
239 procedure TCWWinKeyerUSB.DelLastChar;
240 begin
241   ser.SendByte($8)
242 end;
243 
244 procedure TCWWinKeyerUSB.SetMixManSpeed(min,max : Word);
245 begin
246   ser.SendByte(5);
247   ser.SendByte(min);
248   ser.SendByte(max)
249 end;
250 
251 procedure TCWWinKeyerUSB.TuneStart;
252 begin
253   ser.SendByte($0B);
254   ser.SendByte(1)
255 end;
256 
257 procedure TCWWinKeyerUSB.TuneStop;
258 begin
259   ser.SendByte($0B);
260   ser.SendByte(0)
261 end;
262 
263 procedure TCWWinKeyerUSB.StopSending;
264 begin
265   ser.SendByte($A)
266 end;
267 
268 procedure TCWWinKeyerUSB.SendText(text : String);
269 var
270   i : Integer;
271   spd : Integer;
272 begin
273   spd  := fSpeed;
274   text := UpperCase(text);
275   if fDebugMode then Writeln('Sending text: ',text);
276   if (Pos('+',text) > 0) or (Pos('-',text) > 0) then
277   begin
278     for i:=1 to Length(text) do
279     begin
280       if text[i] = '+' then
281       begin
282         spd := spd+5;
283         if spd>fMaxSpeed then spd:=fMaxSpeed;
284         ser.SendByte($1C);
285         ser.SendByte(spd);
286         Continue
287       end
288       else begin
289         if text[i] = '-' then
290         begin
291           spd := spd-5;
292           if spd<fMinSpeed then spd:=fMinSpeed;
293           ser.SendByte($1C);
294           ser.SendByte(spd);
295           Continue
296         end
297       end;
298       case text[i] of
299         '/' : begin
300                 ser.SendByte($1B);
301                 ser.SendString('D');
302                 ser.SendString('N');
303               end;
304         '?' : begin
305                 ser.SendByte($1B);
306                 ser.SendString('U');
307                 ser.SendString('D');
308               end;
309         '=' : begin
310                 ser.SendByte($1B);
311                 ser.SendString('B');
312                 ser.SendString('T');
313               end;
314         '.' : begin
315                 ser.SendByte($1B);
316                 ser.SendString('Z');
317                 ser.SendString('M');
318               end;
319         ':' : begin
320                 ser.SendByte($1B);
321                 ser.SendString('K');
322                 ser.SendString('N');
323               end;
324         ';' : begin
325                 ser.SendByte($1B);
326                 ser.SendString('A');
327                 ser.SendString('A');
328               end;
329         '<' : begin
330                 ser.SendByte($1B);
331                 ser.SendString('A');
332                 ser.SendString('R');
333               end;
334         '>' : begin
335                 ser.SendByte($1B);
336                 ser.SendString('S');
337                 ser.SendString('K');
338               end;
339         '(' : begin
340                 ser.SendByte($1B);
341                 ser.SendString('K');
342                 ser.SendString('N');
343               end;
344         ')' : begin
345                 ser.SendByte($1B);
346                 ser.SendString('K');
347                 ser.SendString('K');
348               end;
349         '@' : begin
350                 ser.SendByte($1B);
351                 ser.SendString('A');
352                 ser.SendString('C');
353               end;
354 {        'ß' : begin
355                 ser.SendByte($1B);
356                 ser.SendString('S');
357                 ser.SendString('Z');
358               end;
359         'Ü' : begin
360                 ser.SendByte($1B);
361                 ser.SendString('I');
362                 ser.SendString('M');
363               end;
364         'Ö' : begin
365                 ser.SendByte($1B);
366                 ser.SendString('O');
367                 ser.SendString('E');
368               end;
369         'Ä' : begin
370                 ser.SendByte($1B);
371                 ser.SendString('A');
372                 ser.SendString('A');
373               end
374             }
375         else
376           Ser.SendString(text[i])
377       end //case
378     end;
379     ser.SendByte($1F)
380   end
381   else
382     ser.SendString(text)
383 end;
384 
385 procedure TCWWinKeyerUSB.Close;
386 begin
387   ser.SendByte(0);
388   ser.SendByte(3); // close keyer
389   if fDebugMode then Writeln('WinKeyer closed');
390   ser.CloseSocket;
391   fActive := False
392 end;
393 
394 destructor TCWWinKeyerUSB.Destroy;
395 begin
396   if fActive then
397     Close();
398   FreeAndNil(ser)
399 end;
400 
401 constructor TCWDaemon.Create;
402 begin
403   fActive       := False;
404   fDebugMode    := False;
405   udp           := TLUDPComponent.Create(nil);
406   fMinSpeed     := 5;
407   fMaxSpeed     := 60
408 end;
409 
410 procedure TCWDaemon.Open;
411 begin
412   if fDebugMode then
413   begin
414     Writeln('address: ',fDevice);
415     Writeln('port:    ',fPort)
416   end;
417   udp.Host := fDevice;
418   udp.Port := StrToInt(fPort);
419   fActive  := udp.Connect;
420 
421   SetSpeed(fSpeed)
422 end;
423 
424 procedure TCWDaemon.SetSpeed(speed : Word);
425 begin
426   fSpeed := speed;
427   if fActive then
428     udp.SendMessage(Chr(27)+'2'+IntToStr(speed))
429 end;
430 
GetSpeednull431 function TCWDaemon.GetSpeed  : Word;
432 begin
433   Result := fSpeed
434 end;
435 
TCWDaemon.GetStatusnull436 function TCWDaemon.GetStatus : TKeyStatus;
437 begin
438   Result := ksBusy //not implemented, yet
439 end;
440 
441 procedure TCWDaemon.DelLastChar;
442 begin
443   //not implemented
444 end;
445 
446 procedure TCWDaemon.SetMixManSpeed(min,max : Word);
447 begin
448   //not supported in cwdaemon
449 end;
450 
451 procedure TCWDaemon.TuneStart;
452 begin
453   if fActive then
454     udp.SendMessage(Chr(27)+'c10')
455 end;
456 
457 procedure TCWDaemon.TuneStop;
458 begin
459   if fActive then
460     udp.SendMessage(Chr(27)+'c0')
461 end;
462 
463 procedure TCWDaemon.StopSending;
464 begin
465   if fActive then
466     udp.SendMessage(Chr(27)+'4')
467 end;
468 
469 procedure TCWDaemon.SendText(text : String);
470 var
471   i   : Integer;
472   spd : Word;
473   old_spd : Word = 0;
474 begin
475   if not fActive then
476     exit;
477 
478   text := UpperCase(Trim(text));
479   if text = '' then
480     exit;
481   spd     := fSpeed;
482   old_spd := spd;
483   if (Pos('+',text) > 0) or (Pos('-',text) > 0) then
484   begin
485     for i:=1 to Length(text) do
486     begin
487       if text[i] = '+' then
488       begin
489         spd := spd+5;
490         if spd>fMaxSpeed then spd:=fMaxSpeed;
491         udp.SendMessage(Chr(27)+'2'+IntToStr(spd))
492       end
493       else begin
494         if text[i] = '-' then
495         begin
496           spd := spd-5;
497           if spd<fMinSpeed then spd:=fMinSpeed;
498           udp.SendMessage(Chr(27)+'2'+IntToStr(spd))
499         end
500         else
501           udp.SendMessage(text[i])
502       end
503     end;
504     udp.SendMessage(Chr(27)+'2'+IntToStr(old_spd))
505   end
506   else
507     udp.SendMessage(text)
508 end;
509 
510 procedure TCWDaemon.Close;
511 begin
512   if udp.Connected then
513     udp.Disconnect;
514   fActive := False
515 end;
516 
517 destructor TCWDaemon.Destroy;
518 begin
519   if fActive then
520     Close();
521   FreeAndNil(udp)
522 end;
523 
524 constructor TCWK3NG.Create;
525 begin
526   fActive       := False;
527   fDebugMode    := False;
528   ser           := TBlockserial.Create;
529   ser.LinuxLock := False;
530   fMinSpeed     := 5;
531   fMaxSpeed     := 60
532 end;
533 
534 procedure TCWK3NG.Open;
535 var
536   rec : byte;
537 begin
538   if fActive then Close();
539 
540   if fDebugMode then Writeln('Device: ',fDevice);
541   ser.RaiseExcept := False;
542   ser.Connect(fDevice);
543   ser.Config(fPortSpeed,8,'N',2,false,false);
544   ser.DTR := False;
545   ser.RTS := False;
546   fLastErrNr := ser.LastError;
547   fLastErrSt := ser.LastErrorDesc;
548   if fDebugMode then
549   begin
550     Writeln('Last error nr:  ',fLastErrNr);
551     Writeln('Last error desc:',fLastErrSt)
552   end;
553   if LastErrNr > 0 then
554     exit;
555   fActive := True;
556   SetSpeed(fSpeed)
557 end;
558 
559 procedure TCWK3NG.SetSpeed(speed : Word);
560 begin
561   Writeln(Speed);
562   fSpeed := speed;
563   ser.SendByte($5C);
564   ser.SendByte($57);
565   ser.SendString(IntToStr(speed));
566   ser.SendString(CR)
567 end;
568 
GetSpeednull569 function TCWK3NG.GetSpeed  : Word;
570 begin
571   Result := fSpeed
572 end;
573 
TCWK3NG.GetStatusnull574 function TCWK3NG.GetStatus : TKeyStatus;
575 begin
576   Result := ksBusy //not implemented, yet
577 end;
578 
579 procedure TCWK3NG.DelLastChar;
580 begin
581   //not implemented
582 end;
583 
584 procedure TCWK3NG.SetMixManSpeed(min,max : Word);
585 begin
586   //not supported
587 end;
588 
589 procedure TCWK3NG.TuneStart;
590 begin
591   ser.SendByte($5C);
592   ser.SendByte($54)
593 end;
594 
595 procedure TCWK3NG.TuneStop;
596 begin
597   StopSending
598 end;
599 
600 procedure TCWK3NG.StopSending;
601 begin
602   if fActive then
603   begin
604     ser.SendByte($5C);
605     ser.SendByte($5C)
606   end
607 end;
608 
609 procedure TCWK3NG.SendText(text : String);
610 
611   procedure ChangeSpeed(spd : Word);
612   begin
613     ser.SendByte($5C);
614     ser.SendByte($57);
615     ser.SendString(IntToStr(spd));
616     ser.SendString(CR)
617   end;
618 
619 var
620   i   : Integer;
621   spd : Word;
622   old_spd : Word = 0;
623 begin
624   if not fActive then
625     exit;
626 
627   text := UpperCase(Trim(text));
628   if text = '' then
629     exit;
630   spd     := fSpeed;
631   old_spd := spd;
632   if (Pos('+',text) > 0) or (Pos('-',text) > 0) then
633   begin
634     for i:=1 to Length(text) do
635     begin
636       if text[i] = '+' then
637       begin
638         spd := spd+5;
639         if spd>fMaxSpeed then spd:=fMaxSpeed;
640         ChangeSpeed(spd)
641       end
642       else begin
643         if text[i] = '-' then
644         begin
645           spd := spd-5;
646           if spd<fMinSpeed then spd:=fMinSpeed;
647           ChangeSpeed(spd)
648         end
649         else
650           ser.SendString(text[i])
651       end
652     end;
653     ChangeSpeed(old_spd)
654   end
655   else
656     ser.SendString(text)
657 end;
658 
659 procedure TCWK3NG.Close;
660 begin
661   if fDebugMode then Writeln('K3NG keyer closed');
662   ser.CloseSocket;
663   fActive := False
664 end;
665 
666 
667 destructor TCWK3NG.Destroy;
668 begin
669   if fActive then
670     Close();
671   FreeAndNil(ser)
672 end;
673 
674 constructor TCWHamLib.Create;
675 begin
676   fActive       := False;
677   fDebugMode    := False;
678   tcp           := TLTCPComponent.Create(nil);
679   tcp.ReuseAddress:= True;
680   tcp.OnReceive := @OnReceived;
681   tcp.OnConnect := @OnHamLibConnect;
682   tcp.OnError   := @onHamLibError;
683   fMinSpeed     := 5;
684   fMaxSpeed     := 60
685 end;
686 
687 procedure TCWHamLib.OnHamLibConnect(aSocket: TLSocket);
688 begin
689   fActive := True;
690   if DebugMode then
691      Writeln('CWint connected to hamlib');
692 
693   tcp.SendMessage('fmv'+LineEnding);
694   SetSpeed(fSpeed)
695 end;
696 
697 procedure TCWHamLib.OnHamLibError(const msg: AnsiString; aSocket: TLSocket);
698 begin
699   if DebugMode then
700      Writeln('CWint connect to hamlib FAILED: '+msg);
701 
702   fActive := False
703 end;
704 
705 procedure TCWHamLib.Open;
706 begin
707   if fActive then Close();
708 
709   if fDebugMode then
710   begin
711     Writeln('address: ',fDevice);
712     Writeln('port:    ',fPort)
713   end;
714   tcp.Host := fDevice;
715   tcp.Port := StrToInt(fPort);
716   tcp.Connect(fDevice,StrToInt(fPort));
717 end;
718 
719 procedure TCWHamLib.OnReceived(aSocket: TLSocket);
720 begin
721   if aSocket.GetMessage(Rmsg) > 0 then
722    begin
723     Rmsg := StringReplace(Rmsg,LineEnding,' ',[rfReplaceAll]);
724     if DebugMode then Writeln('HLresp MSG:',Rmsg,':');
725    end;
726 end;
727 procedure TCWHamLib.WaitMorse;
728 begin
729   tcp.SendMessage('\wait_morse' +LineEnding);
730 end;
731 
732 procedure TCWHamLib.SetSpeed(speed : Word);
733 begin
734   if Speed>fMaxSpeed then speed:= fMaxSpeed;
735   if Speed<fMinSpeed then speed:= fMinSpeed;
736   fSpeed := speed;
737   if fActive then
738     tcp.SendMessage('L KEYSPD '+IntToStr(speed)+LineEnding);
739   if fDebugMode then
740     Writeln('CW speed changed to:',fSpeed)
741 end;
742 
GetSpeednull743 function TCWHamLib.GetSpeed  : Word;
744 begin
745   Result := fSpeed
746 end;
747 
TCWHamLib.GetStatusnull748 function TCWHamLib.GetStatus : TKeyStatus;
749 begin
750   Result := ksBusy //not implemented, yet
751 end;
752 
753 procedure TCWHamLib.DelLastChar;
754 begin
755   //not implemented
756 end;
757 
758 procedure TCWHamLib.SetMixManSpeed(min,max : Word);
759 begin
760   //not supported
761 end;
762 
763 procedure TCWHamLib.TuneStart;
764 begin
765   //supported via AM mode
766   frmTRXControl.HLTune(true);
767 end;
768 
769 procedure TCWHamLib.TuneStop;
770 begin
771   //supported via AM mode
772   frmTRXControl.HLTune(false);
773 end;
774 
775 procedure TCWHamLib.StopSending;
776 begin
777   AllowCW := false;
778   //not implemented in hamlib command set
779   //sending 0xFF as text works with Icom
780   tcp.SendMessage('b'+#$0FF+LineEnding);
781   //All chrs are spaces stops cw for kenwood. Empty chrs (max24) in buffer are filled with spaces.
782   // (info by ts480 manual, not tested)
783   tcp.SendMessage('b '+LineEnding);
784 end;
785 
786 procedure TCWHamLib.SendText(text : String);
787 const
788      _REPEATS = 500; //times
789      _TIMEOUT = 20; //x10-milliseconds
790 var
791   c, i,
792   tout,
793   rpt : integer;
794   Wcw : char;
795   dSpd: integer;
796             //-----------------------------------------------------------------------------------
797             Procedure SendToHamlib(t:string);
798             Begin
799                         tout :=_TIMEOUT; //used away in sleep(10) bloks
800                         rpt := _REPEATS;
801 
802                         while ((rpt > 0) and AllowCW) do
803                           Begin
804                              if fDebugMode then  Writeln('HLsend MSG:','b'+t+':');
805                              Rmsg:='';
806                              tcp.SendMessage('b'+t+LineEnding);
807                              dec(rpt);
808                               repeat
809                                 begin
810                                   sleep(10);
811                                   Application.ProcessMessages;
812                                   dec(tout);
813                                 end;
814                               until ((pos('RPRT',Rmsg)>0) or (tout < 1 ));
815                               if fDebugMode then  Writeln('     Ack timeout left: ',tout,'(/',_TIMEOUT,')');
816                               if fDebugMode then  Writeln('     Repeats left: ',rpt,'(/',_REPEATS,')');
817                               if pos('-9',Rmsg)>0 then
818                                 Begin
819                                   dec(rpt);
820                                   sleep(300);
821                                 end
822                                else
823                                 rpt :=0;
824                           end;
825 
826             end;
827             //-----------------------------------------------------------------------------------
828             Procedure ModSpeed(m:char);
829             Begin
830                 if Wcw<>m then WaitMorse;
831                 //check if more m
832                 While ((text[i]=m) and (i<=length(text))) do
833                   Begin
834                     if m='+' then inc(dSpd) else dec(dSpd);
835                     inc(i)
836                   end;
837                 if (i<length(text)) then dec(i); //there is still text left
838                 SetSpeed(fSpeed+dSpd*5);
839                 if fDebugMode then  Writeln(m,' speed with ',dSpd*5);
840                 dSpd:=0;
841             end;
842             //-----------------------------------------------------------------------------------
843 begin
844    if text<>'' then
845      begin
846        Wcw:=#0;
847        dSpd:=0;
848        AllowCW := true;
849         //different rigs support different length of b-command. 10chr should be safe for all
850         c:= length(text);
851         if (c>10) or (pos('+',text)>0) or (pos('-',text)>0) then
852          Begin
853             i := 1;
854             if fDebugMode then  Writeln('Ltr send: ');
855             repeat
856              Begin
857                case text[i] of
858                  '+','-'   : ModSpeed(text[i]);
859                  else
860                              Begin
861                                if fDebugMode then  Writeln('send letter ',i,' ',text[i]);
862                                SendToHamlib(text[i]);
863                                Wcw:=#0;
864                              end;
865                end;
866                inc(i);
867              end;
868             until (i > c);
869          end
870         else
871          SendToHamlib(text);
872       end
873      else  if fDebugMode then  Writeln('Empty message!');
874 end;
875 
876 procedure TCWHamLib.Close;
877 begin
878   if tcp.Connected then
879     tcp.Disconnect;
880   fActive := False
881 end;
882 
883 destructor TCWHamLib.Destroy;
884 begin
885   if fActive then
886     Close();
887   FreeAndNil(tcp);
888   if fDebugMode then
889     Writeln('Keying over HamLib closed')
890 end;
891 
892 end.
893