// =====================================================
//
//   miniLA_win - PWM Decoder
//
//   (c) miniLA Team
//
// =====================================================
//
// This 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 2, or (at your option)
// any later version.
//
// This software 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 package; see the file COPYING.  If not, write to
// the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
// Boston, MA 02111-1307, USA.

unit dlgDecPWM;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Buttons,
  uDecUtils, Spin;

type
  TfrmDecPWM = class(TForm)
    cbChannel: TComboBox;
    Label1: TLabel;
    cbInvert: TCheckBox;
    btnOK: TBitBtn;
    btnCancel: TBitBtn;
    GroupBox1: TGroupBox;
    Label5: TLabel;
    cbBitOrder: TComboBox;
    gbDecodeRange: TGroupBox;
    rbAllData: TRadioButton;
    rbCursors: TRadioButton;
    Label2: TLabel;
    seThresholdHi: TSpinEdit;
    seThresholdLo: TSpinEdit;
    Label3: TLabel;
    Label4: TLabel;
    Label6: TLabel;
    cbSwapLog: TCheckBox;
    procedure btnOKClick(Sender: TObject);
    procedure FormActivate(Sender: TObject);
    procedure btnCancelClick(Sender: TObject);
    procedure seThresholdHiChange(Sender: TObject);
    procedure seThresholdLoChange(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    { Private declarations }
    ChannelSel:  TChannelSel;

    decoded_channel 	: integer;
    channel		: cardinal;
    channel_last	: cardinal;

    function D_i(val:byte):cardinal;
    function D_o(val:cardinal):byte;

    function loc_data_find_change:boolean;
  public
    { Public declarations }
  end;

var
  frmDecPWM: TfrmDecPWM;

implementation

uses
   uUtils,
   uLATypes,
   uDecData,
   uINIFile,
   dlgDecDisplay,
   dlgMain;

{$R *.dfm}

procedure TfrmDecPWM.FormCreate(Sender: TObject);
begin
   cbChannel.ItemIndex  := INIFile.ReadInteger('DEC_PWM','CHANNEL', 0);
   cbInvert.Checked     := INIFile.ReadBool('DEC_PWM','INVERTED', false);
   cbBitOrder.ItemIndex := INIFile.ReadInteger('DEC_PWM','BIT_ORDER', 1);
   seThresholdHi.Value  := INIFile.ReadInteger('DEC_PWM','THRESHOLD_HI', 60);
   seThresholdLo.Value  := INIFile.ReadInteger('DEC_PWM','THRESHOLD_LO', 40);
   cbSwapLog.Checked    := INIFile.ReadBool('DEC_PWM','SWAPLOG', false);
   rbAllData.Checked    := INIFile.ReadBool('DEC_PWM','DEC_FULL_RANGE', true);
   rbCursors.Checked	:= not(rbAllData.Checked);
end;


procedure TfrmDecPWM.FormDestroy(Sender: TObject);
begin
   INIFile.WriteInteger('DEC_PWM','CHANNEL', cbChannel.ItemIndex);
   INIFile.WriteBool('DEC_PWM','INVERTED', cbInvert.Checked);
   INIFile.WriteInteger('DEC_PWM','BIT_ORDER', cbBitOrder.ItemIndex);
   INIFile.WriteInteger('DEC_PWM','THRESHOLD_HI', seThresholdHi.Value);
   INIFile.WriteInteger('DEC_PWM','THRESHOLD_LO', seThresholdLo.Value);
   INIFile.WriteBool('DEC_PWM','SWAPLOG', cbSwapLog.Checked);
   INIFile.WriteBool('DEC_PWM','DEC_FULL_RANGE', rbAllData.Checked);
end;


procedure TfrmDecPWM.FormActivate(Sender: TObject);
begin
   ChannelSel := TChannelSel.Create;
   ChannelSel.Add(cbChannel);
end;

procedure TfrmDecPWM.seThresholdHiChange(Sender: TObject);
begin
   seThresholdLo.MaxValue := seThresholdHi.Value - 1;
end;

procedure TfrmDecPWM.seThresholdLoChange(Sender: TObject);
begin
   seThresholdHi.MinValue := seThresholdLo.Value + 1;
end;


function TfrmDecPWM.D_i(val:byte):cardinal;
begin
   result := val shl decoded_channel;
end;

function TfrmDecPWM.D_o(val:cardinal):byte;
begin
   result := (val shr decoded_channel) and $1;
end;

function TfrmDecPWM.loc_data_find_change:boolean;
var
   read_err	: boolean;
begin
   channel_last := channel;
   read_err := data_find_change(D_i(1));
   channel := D_o(sample);
   result := read_err;
end;


procedure TfrmDecPWM.btnOKClick(Sender: TObject);
var
   read_err	: boolean;

   state	: (ST_IDLE, ST_DATA_FE, ST_DATA_RE);
   bit_cnt	: byte;
   rec_bit	: byte;
   rec_byte	: byte;
   rec_complete	: boolean;
   rec_str	: string;
   add_bit	: boolean;

   len100	: int64;
   re_ptr_last	: int64;
   re_ptr	: int64;
   fe_ptr	: int64;
   r_f_ratio	: integer;

   lsb_first	: boolean;

   err_msg	: string;

begin
   err_msg := '';
   frmDecDisplay.lbResults.Clear;

   decoded_channel := ChannelSel.GetChannelNumber(cbChannel);
   if (decoded_channel < 0) then
      err_msg := 'channel';

   case cbBitOrder.ItemIndex of
      0: lsb_first := true;
      1: lsb_first := false;
   else
      err_msg := 'bit order';
      lsb_first := true;
   end;

   if err_msg <> '' then
    begin
      MessageDlg('Invalid '+err_msg, mtError,[mbOK],0);
      exit;
    end;

   ChannelSel.Free;
   Hide;
   frmMain.StatusBar1.Panels[5].Text := 'Decoding PWM protocol ...';
   frmMain.StatusBar1.Repaint;

   sample_inv := 0;

   if cbInvert.Checked then
      sample_inv := sample_inv or D_i(1);

   if rbAllData.Checked then
      data_init(0, frmMain.LogPanel.Display.DataHold.MaxPosition)	// initialize sample read routine
   else
      data_init(frmMain.LogPanel.Cursors[0].Position,
		frmMain.LogPanel.Cursors[1].Position);

   frmDecDisplay.lbResults.Items.Add(Format('%d (%ss) start of decoding', [sample_n, TmToStr(time_p,2)]));

   bit_cnt := 0;
   state := ST_IDLE;
   re_ptr := sample_n;
   len100 := 1;							// default value to avoid division by zero problems

   read_err := false;

   // get initial value
   channel := D_o(sample);

   while (not read_err) do
    begin
      read_err := read_err or loc_data_find_change();

      // start of bit - rising edge
      if ((channel_last=0) and (channel<>0)) or read_err then
       begin
	 re_ptr_last := re_ptr;
	 re_ptr := sample_n;

	 if state = ST_IDLE then
	  begin
	    rec_byte := 0;
	    bit_cnt := 0;
	  end
	 else
	  begin
	    rec_complete := false;
	    add_bit := false;

            r_f_ratio := round(100 * (fe_ptr-re_ptr_last)/(re_ptr-re_ptr_last));

	    // probably end of data
	    if r_f_ratio < seThresholdLo.Value div 4 then
	     begin
	       // compare to last known bit width
	       if (round(100*(fe_ptr-re_ptr_last)/len100) > seThresholdHi.Value) xor cbSwapLog.Checked then
		  rec_bit := 1
	       else
		  rec_bit := 0;

	       rec_complete := true;
	       add_bit := true;
	     end

	    // standart 0
	    else if r_f_ratio <= seThresholdLo.Value then
	     begin
	       if not cbSwapLog.Checked then
		  rec_bit := 0
	       else
		  rec_bit := 1;

	       len100 := re_ptr - re_ptr_last;
	       add_bit := true;
	      end

	    // preamble
	    else if r_f_ratio < seThresholdHi.Value then
	     begin
	       if (bit_cnt<>0) and not read_err then
		begin
		  frmDecDisplay.lbResults.Items.Add(Format('%d (%ss) Error, unexpected preamble', [sample_n, TmToStr(time_p,2)]));
		  rec_complete := true;
		end;
	     end

	    // standard 1
	    else if not read_err then
	     begin
	       if not cbSwapLog.Checked then
		  rec_bit := 1
	       else
		  rec_bit := 0;

	       len100 := re_ptr - re_ptr_last;
	       add_bit := true;
	     end;

	    // partial data
	    if read_err then
	     begin
		rec_complete := true;
	     end;


	    if add_bit then
	     begin
	       if lsb_first then
		  rec_byte := rec_byte or (rec_bit shl bit_cnt)
	       else
		  rec_byte := (rec_byte shl 1) or rec_bit;

	       inc(bit_cnt);
	     end;

	    if (bit_cnt = 8) or rec_complete then
	     begin
	       if (rec_byte >= 31) then
		  rec_str :=chr(rec_byte)
	       else
		  rec_str := ' ';

	       if bit_cnt<>8 then
		  frmDecDisplay.lbResults.Items.Add(Format('%d (%ss) (partial data - %d bits) 0x%.2X %s ', [sample_n, TmToStr(time_p,2),bit_cnt,rec_byte,rec_str]))
	       else
		  frmDecDisplay.lbResults.Items.Add(Format('%d (%ss) 0x%.2X %s ', [sample_n, TmToStr(time_p,2),rec_byte,rec_str]));

	       bit_cnt := 0;
	       rec_byte := 0;
	     end;
	  end;

	 state := ST_DATA_FE;
       end;

      // falling edge
      if ((channel_last<>0) and (channel=0) and (state<>ST_IDLE)) then
       begin
	 fe_ptr := sample_n;
	 state := ST_DATA_RE;
       end;


    end;

   frmDecDisplay.lbResults.Items.Add(Format('%d (%ss) end of decoding', [sample_n, TmToStr(time_p,2)]));   ModalResult := mrOK;

   ModalResult := mrOK;
   frmDecDisplay.Caption := 'Decoded data - PWM';
   frmDecDisplay.Show;
   frmMain.StatusBar1.Panels[5].Text := '';
   frmMain.StatusBar1.Repaint;
end;


procedure TfrmDecPWM.btnCancelClick(Sender: TObject);
begin
   ChannelSel.Free;
end;





end.

