/* *****************************************************************************
 * File:      bipolar.c
 * Date:      10 Apr 2026
 * Author:    Andrew Levido
 *
 * Copyright 2026 Imbrius Pty Ltd - All Rights Reserved
 *
 ******************************************************************************/

/* Includes ------------------------------------------------------------------*/
#include "bipolar.h"
#include "analog.h"
#include "analyser.h"
#include "analyser_helper.h"
#include "cli.h"
#include "screen.h"
#include <stdio.h>
#include "beep.h"

/* Defines -------------------------------------------------------------------*/

/* Typedefs & Enumerations ---------------------------------------------------*/
typedef struct {
  int32_t vbe_mv;
  int32_t ic_vbe_ua;
  int32_t hfe;
  int32_t ic_hfe_ua;
} ax_bjt_vf_hfe_t;

/* Global Variables ----------------------------------------------------------*/

/* Private Functions ---------------------------------------------------------*/

void ax_find_hfe(int32_t ib_lo_na, int32_t ib_hi_na, int32_t target, ax_bjt_vf_hfe_t * data)
{
  int32_t ib_na;
  int32_t iterations = 0;
  int32_t ic_ua;

  data->hfe = 0;
  if(ib_lo_na >= ib_hi_na) { return; }
  
  #ifdef AX_VERBOSE
  cli_printf("\t#      Ic (uA)      Ib (nA)");
  #endif
  ib_na = (ib_lo_na + ib_hi_na) / 2;
  do {
    if((ib_na > 250000) || (ib_na < -250000)) { 
      an_set_drive_ua(ib_na / 1000); }
    else { 
       an_set_drive_na(ib_na); 
    }
    ic_ua = an_get_load_ua();
    #ifdef AX_VERBOSE
    cli_printf("\t%2i   %+8i     %+8i", iterations, ic_ua, ib_na);
    #endif
    if(ic_ua < target - AX_BJT_HFE_TOL_IC) {
      // increase ib
      ib_lo_na = ib_na;
      ib_na = (ib_lo_na + ib_hi_na) / 2;
    }
    else if(ic_ua > target + AX_BJT_HFE_TOL_IC) {
      ib_hi_na = ib_na;
      ib_na = (ib_lo_na + ib_hi_na) / 2;
    }
    else { break; }
  } while(iterations++ < AX_BJT_HFE_MAX_ITER);
  if(iterations < AX_BJT_HFE_MAX_ITER) {
    data->hfe = (ic_ua * 1000) / ib_na ;
    data->ic_hfe_ua = ic_ua;
  }
}

void ax_find_hfe_npn(lead_t base, lead_t emitter, lead_t collector, ax_bjt_vf_hfe_t * data)
{
  // set up tests
  an_set_power(false);
  an_set_load_mv(12000);
  an_set_matrix(NODE_POWER, emitter);
  an_set_matrix(NODE_LOAD, collector);
  an_set_matrix(NODE_DRIVE, base);

  // Which base current range?
  an_set_drive_ua(250); // 250uA
  if(an_get_load_ua() < (AX_BJT_HFE_TEST_IC - AX_BJT_HFE_TOL_IC)) {
    ax_find_hfe(AX_BJT_HFE_MID_IB, AX_BJT_HFE_HI_IB, AX_BJT_HFE_TEST_IC, data);
  }
  else {
    ax_find_hfe(AX_BJT_HFE_LO_IB, AX_BJT_HFE_MID_IB, AX_BJT_HFE_TEST_IC, data);
  }
  an_set_matrix_all_open();
}

void ax_find_hfe_pnp(lead_t base, lead_t emitter, lead_t collector, ax_bjt_vf_hfe_t * data)
{
  // set up tests
  an_set_power(true);
  an_set_load_mv(0);
  an_set_matrix(NODE_POWER, emitter);
  an_set_matrix(NODE_LOAD, collector);
  an_set_matrix(NODE_DRIVE, base);

  // Which base current range?
  an_set_drive_ua(-250); // 250uA
  if(an_get_load_ua() > -(AX_BJT_HFE_TEST_IC - AX_BJT_HFE_TOL_IC)) {
    ax_find_hfe(-AX_BJT_HFE_HI_IB, -AX_BJT_HFE_MID_IB, -AX_BJT_HFE_TEST_IC, data);
  }
  else {
    ax_find_hfe(-AX_BJT_HFE_MID_IB, -AX_BJT_HFE_LO_IB, -AX_BJT_HFE_TEST_IC, data);
  }
  an_set_matrix_all_open();
}

void ax_bjt_get_vbe(lead_t base, lead_t emitter, bool pnp, ax_bjt_vf_hfe_t * data)
{
  data->ic_vbe_ua = pnp ? -AX_BJT_VBE_TEST_I : AX_BJT_VBE_TEST_I;
  an_set_drive_ua(data->ic_vbe_ua);
  an_set_power(pnp ? true : false);
  an_set_matrix(NODE_DRIVE,base);
  an_set_matrix(NODE_POWER, emitter);
  data->vbe_mv = ax_get_lead_mv(base) - ax_get_lead_mv(emitter);
  // Switch off
  an_set_matrix_all_open();

}


/* Public Functions ----------------------------------------------------------*/

void ax_test_bjt_npn(lead_t base, lead_t emitter, lead_t collector, bool fwd)
{
  ax_bjt_vf_hfe_t bjt;
  screen_desc_t screen;
  char value_1 [10];
  char value_2 [10];

  // Get Vbe and Hfe
  cli_printf("\tBJT NPN or Darlington NPN?");
  ax_bjt_get_vbe(base, emitter, false, &bjt);
  ax_find_hfe_npn(base, emitter, collector, &bjt);

  // Common to all
  ax_print_flt(value_1, bjt.vbe_mv);
  ax_print_flt(value_2, bjt.ic_vbe_ua);
  sprintf(ax_line_1_buffer, "Vbe = %sV @ %smA", value_1, value_2);
  screen.string1_override = ax_line_1_buffer;
  screen.top_node_id = (scrn_node_id_t)collector;
  screen.left_node_id = (scrn_node_id_t)base;
  screen.bottom_node_id = (scrn_node_id_t)emitter;

  // Darlington ?
  if(bjt.vbe_mv > AX_BJT_VBE_DARL_V) {
    cli_printf("\t-> NPN Darlington");
    cli_printf("\t   Base = %s, Emitter = %s, Collector = %s", ax_lead_2_text(base), ax_lead_2_text(emitter), ax_lead_2_text(collector));
    screen.screen = fwd == true ? &scrn_darlington_npn_diode : &scrn_darlington_npn;
  }
  else {
    cli_printf("\t-> NPN BJT");
    cli_printf("\t   Base = %s, Emitter = %s, Collector = %s", ax_lead_2_text(base), ax_lead_2_text(emitter), ax_lead_2_text(collector));
    screen.screen = fwd == true ? &scrn_bjt_npn_diode : &scrn_bjt_npn;
  }
  cli_printf("\t* Vbe = %+i mV @ Ib = %+i µA", bjt.vbe_mv, bjt.ic_vbe_ua);
  if(bjt.hfe != 0) {
    cli_printf("\t* hFE = %i @ Ic = %+i µA", bjt.hfe, bjt.ic_hfe_ua);
    ax_print_flt(value_2, bjt.ic_hfe_ua);
    sprintf(ax_line_2_buffer, "hFE = %i @ %smA", bjt.hfe, value_2);
    screen.string2_override = ax_line_2_buffer;
  }
  else {
    cli_printf("\t* Could not measure hFE");
    screen.string2_override = "hFE not Measured";
  }
  scrn_draw(&screen);
  beep(A6, BEEP_SHORT);
}

void ax_test_bjt_pnp(lead_t base, lead_t emitter, lead_t collector, bool fwd)
{
  ax_bjt_vf_hfe_t bjt;
  screen_desc_t screen;
  char value_1 [10];
  char value_2 [10];

  // Get Vbe and Hfe
  cli_printf("\tBJT PNP or Darlington PNP?");
  ax_bjt_get_vbe(base, emitter, true, &bjt);

  ax_find_hfe_pnp(base, emitter, collector, &bjt);

  // Common to all
  ax_print_flt(value_1, bjt.vbe_mv);
  ax_print_flt(value_2, bjt.ic_vbe_ua);
  sprintf(ax_line_1_buffer, "Vbe = %sV @ %smA", value_1, value_2);
  screen.string1_override = ax_line_1_buffer;
  screen.top_node_id = (scrn_node_id_t)emitter;
  screen.left_node_id = (scrn_node_id_t)base;
  screen.bottom_node_id = (scrn_node_id_t)collector;

  // Darlington ?
  if(bjt.vbe_mv < -AX_BJT_VBE_DARL_V) {
    cli_printf("\t-> PNP Darlington");
    cli_printf("\t   Base = %s, Emitter = %s, Collector = %s", ax_lead_2_text(base), ax_lead_2_text(emitter), ax_lead_2_text(collector));
    screen.screen = fwd == true ? &scrn_darlington_pnp_diode : &scrn_darlington_pnp;
  }
  else {
    cli_printf("\t-> PNP BJT");
    cli_printf("\t   Base = %s, Emitter = %s, Collector = %s", ax_lead_2_text(base), ax_lead_2_text(emitter), ax_lead_2_text(collector));
    screen.screen = fwd == true ? &scrn_bjt_pnp_diode : &scrn_bjt_pnp;
  }
  cli_printf("\t* Vbe = %+i mV @ Ib = %+i µA", bjt.vbe_mv, bjt.ic_vbe_ua);
  if(bjt.hfe != 0) {
    cli_printf("\t* hFE = %i @ Ic = %+i µA", bjt.hfe, bjt.ic_hfe_ua);
    ax_print_flt(value_2, bjt.ic_hfe_ua);
    sprintf(ax_line_2_buffer, "hFE = %i @ %smA", bjt.hfe, value_2);
    screen.string2_override = ax_line_2_buffer;
  }
  else {
    cli_printf("\t* Could not measure hFE");
    screen.string2_override = "hFE not Measured";
  }

  scrn_draw(&screen);
  beep(A6, BEEP_SHORT);

}

/* End bipolar */