/* *************************************************************************************************
 * File:      analyser.c
 * Date:      31 Mar 2026
 * Author:    Andrew Levido
 *
 * Copyright 2026 Imbrius Pty Ltd - All Rights Reserved
 *
 **************************************************************************************************/

/* Includes --------------------------------------------------------------------------------------*/
#include "analyser.h"
#include "analog.h"
#include "analyser_helper.h"
#include "cli.h"
#include "diode.h"
#include "bipolar.h"
#include "fet.h"
#include "screen.h"
#include "thyristor.h"
#include <stdbool.h>
#include "beep.h"

/* Defines ---------------------------------------------------------------------------------------*/
// ID test result flags
#define AX_FLAG_COND_P                  0x01      // Conducts under positive drive
#define AX_FLAG_COND_N                  0x02      // Conducts under negative drive

/* Typedefs & Enumerations -----------------------------------------------------------------------*/

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

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

/** Perform identity test
 * @returns                 id flags
 */
uint16_t ax_identity_check(void);

/**  Get ID flags for one particular arrangement of leads
* @param lead_from          From lead
* @param lead_tp            To lead
* @param lead_cntrl         Control Lead
*/
uint8_t ax_get_id(lead_t lead_from, lead_t lead_to, lead_t lead_cntrl);

/** Discriminate between BJT NPN and JFET N-CH and identify emitter/source and collector/drain 
* @param base_gate          Base or gate
* @param lead_1             emitter/source or collector/drain 
* @param lead_2             emitter/source or collector/drain 
*/
void ax_disc_bjtn_jfetn(lead_t base_gate, lead_t lead_1, lead_t lead_2);

/** Discriminate between BJT PNP and JFET P-CH and identify emitter/source and collector/drain 
* @param base_gate          Base or gate
* @param lead_1             emitter/source or collector/drain 
* @param lead_2             emitter/source or collector/drain 
*/
void ax_disc_bjtp_jfetp(lead_t base_gate, lead_t lead_1, lead_t lead_2);

/** Discriminate between Diode Pair KK and Thyristor
* @param cathode_kk         Cathode or common cathode
* @param lead_1             anode/gate or A1/A2
* @param lead_2             anode/gate or A1/A2
*/
void ax_disc_diode_kk_thyristor(lead_t cathode_kk, lead_t lead_1, lead_t lead_2);

/** Discriminate between MFET Enh NCh, MFET Dep NCh, IGBT + FWD
* @param gate               Gate
* @param src_em             emitter/source 
* @param drn_col            collector/drain 
*/
void ax_disc_mfetn_igbt(lead_t gate, lead_t src_em, lead_t drn_col);

/** Discriminate Triac leads
* @param gate_m1         gate/m1
* @param m1_gate         gate/m1
* @param m2              m2
*/
bool ax_is_triac(lead_t gate_m1, lead_t m1_gate, lead_t m2);

/** Identify short circuit
* @param lead_1             emitter/source or collector/drain 
* @param lead_2             emitter/source or collector/drain 
*/
bool ax_is_short(lead_t lead_1, lead_t lead_2);

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

void ax_analyse(void)
{
  uint16_t id_flags = 0;
  uint8_t short_flags = 0;
  screen_desc_t screen;

  // Display the wait screen
  screen.screen = &scrn_wait;
  screen.string1_override = NULL,
  screen.string2_override = NULL,
  scrn_draw(&screen);


  // First pass of identification - conductivity with +ve and -ve drive
  id_flags = ax_identity_check();
  cli_printf("\n\tInitial Test Flags: 0x%03x", id_flags);


  // If any pair of leads show conductivity in both directions check for short
 if((id_flags & 0x00f) == 0x00f)  {
    if(ax_is_short(LEAD_RED, LEAD_GREEN)) {
      short_flags |= 0x01;
      cli_printf("\t-> Short: Red <-> Green");
      screen.string2_override = "Red Lead to Green Lead";
    }
  }
  if((id_flags & 0x0f0) == 0x0f0)  {
    if(ax_is_short(LEAD_GREEN, LEAD_BLUE)) {
      short_flags |= 0x02;
       cli_printf("\t-> Short: Green <-> Blue");
       screen.string2_override = "Green Lead to Blue Lead";
    }
  }
  if((id_flags & 0xf00) == 0xf00)  {
    if(ax_is_short(LEAD_BLUE, LEAD_RED)) {
      short_flags |= 0x04;
       cli_printf("\t-> Short: Blue <-> Red");
       screen.string2_override = "Blue Lead to Red Lead";
    }
  }

  if(short_flags != 0) {
    if(short_flags == 0x07) {
      screen.string2_override = "All Three Leads";
    }
    screen.string1_override = "Short Circuit";
    screen.screen = &scrn_error;
    scrn_draw(&screen);
    beep(C5, BEEP_LONG);
    return;
  }


  // Check for Triac
  // (Triacs don't seem to have a consistent signature beyond b2b diode Gate-M1)
  if((id_flags & 0x00f) == 0x00f)  {
    if(ax_is_triac(LEAD_RED, LEAD_GREEN, LEAD_BLUE)) { return; }  
  }
  if((id_flags & 0x0f0) == 0x0f0)  {
    if(ax_is_triac(LEAD_GREEN, LEAD_BLUE, LEAD_RED)) { return; }  
  }
  if((id_flags & 0xf00) == 0xf00)  {
    if(ax_is_triac(LEAD_BLUE, LEAD_RED, LEAD_GREEN)) { return; }
  }

  // Now decide what the DUT is based on the ID flags. Some are difinitive and some require
  // further discrimination checks
  switch(id_flags) {
    // No continuity between any leads
    case 0x000: 
    cli_printf("\t-> Open Circuit Three Leads"); 
      screen.screen = &scrn_error;
      screen.string1_override = "Open Circuit",
      screen.string2_override = "All Three Leads";
      scrn_draw(&screen);
      beep(C5, BEEP_LONG);
      break;
    // Diode Junction
    case 0x003:
      ax_test_diode(LEAD_RED, LEAD_GREEN);
      break;
    case 0x00c:
      ax_test_diode(LEAD_GREEN, LEAD_RED);
      break;
    case 0x030:
      ax_test_diode(LEAD_GREEN, LEAD_BLUE);
      break;
    case 0x0c0:
      ax_test_diode(LEAD_BLUE, LEAD_GREEN);
      break;
    case 0x300:
      ax_test_diode(LEAD_BLUE, LEAD_RED);
      break;
    case 0xc00:
      ax_test_diode(LEAD_RED, LEAD_BLUE);
      break;
    // Diode Pair AK
    case 0xcc3:
      ax_test_diode_ak(LEAD_RED, LEAD_BLUE, LEAD_GREEN);
      break;
    case 0x33c:
      ax_test_diode_ak(LEAD_GREEN, LEAD_BLUE, LEAD_RED);
      break;
    case 0xc3c:
      ax_test_diode_ak(LEAD_GREEN, LEAD_RED, LEAD_BLUE);
      break;
    case 0x3c3:
      ax_test_diode_ak(LEAD_BLUE, LEAD_RED, LEAD_GREEN);
      break; 
    case 0x3cc:
      ax_test_diode_ak(LEAD_BLUE, LEAD_GREEN, LEAD_RED);
      break;
    case 0xc33:
      ax_test_diode_ak(LEAD_RED, LEAD_GREEN, LEAD_BLUE);
      break;
    // Diode Pair KK or Thyristor
    case 0xc30:
      ax_disc_diode_kk_thyristor(LEAD_BLUE, LEAD_RED, LEAD_GREEN);
      break;
    case 0x30c:
      ax_disc_diode_kk_thyristor(LEAD_RED, LEAD_GREEN, LEAD_BLUE);
      break;
    case 0x0c3:
      ax_disc_diode_kk_thyristor(LEAD_GREEN, LEAD_BLUE, LEAD_RED);
      break;
    // Diode Pair AA
    case 0x3c0:
      ax_test_diode_aa(LEAD_RED, LEAD_BLUE, LEAD_GREEN);
      break;
    case 0xc03:
      ax_test_diode_aa(LEAD_GREEN, LEAD_RED, LEAD_BLUE);
      break;
    case 0x03c:
      ax_test_diode_aa(LEAD_BLUE, LEAD_GREEN, LEAD_RED);
      break;
       // Diode Pair B2B or Short
    case 0x00f:
      ax_test_diode_b2b(LEAD_RED, LEAD_GREEN, LEAD_BLUE);
      break;
    case 0x0f0:
      ax_test_diode_b2b(LEAD_GREEN, LEAD_BLUE, LEAD_RED);
      break;
    case 0xf00:
      ax_test_diode_b2b(LEAD_BLUE, LEAD_RED, LEAD_GREEN);
      break;
    // BJT NPN or J-FET N-CH
    case 0x3c5:
      ax_disc_bjtn_jfetn(LEAD_BLUE, LEAD_RED, LEAD_GREEN);
      break;
    case 0xc53:
      ax_disc_bjtn_jfetn(LEAD_RED, LEAD_GREEN, LEAD_BLUE);
      break;
    case 0x53c:
      ax_disc_bjtn_jfetn(LEAD_GREEN, LEAD_BLUE, LEAD_RED);
      break;     
    // BJT PNP or J-FET P-CH
    case 0xc3a:
      ax_disc_bjtp_jfetp(LEAD_BLUE, LEAD_RED, LEAD_GREEN);
      break;
    case 0x3ac:
      ax_disc_bjtp_jfetp(LEAD_RED, LEAD_GREEN, LEAD_BLUE);
      break;
    case 0xac3:
      ax_disc_bjtp_jfetp(LEAD_GREEN, LEAD_BLUE, LEAD_RED);
      break;
    // Darlington NPN no FWD
    case 0x3c1: 
      ax_test_bjt_npn(LEAD_BLUE, LEAD_GREEN, LEAD_RED, false);
      break;
    case 0x3c4: 
      ax_test_bjt_npn(LEAD_BLUE, LEAD_RED, LEAD_GREEN, false);
      break;    
    case 0xc13: 
      ax_test_bjt_npn(LEAD_RED, LEAD_BLUE, LEAD_GREEN, false);
      break;
    case 0xc43: 
      ax_test_bjt_npn(LEAD_RED, LEAD_GREEN, LEAD_BLUE, false);
      break;
    case 0x13c: 
      ax_test_bjt_npn(LEAD_GREEN, LEAD_RED, LEAD_BLUE, false);
      break;
    case 0x43c: 
      ax_test_bjt_npn(LEAD_GREEN, LEAD_BLUE, LEAD_RED, false);
      break;
    // Darlington PNP no FWD
    case 0xc38: 
      ax_test_bjt_pnp(LEAD_BLUE, LEAD_GREEN, LEAD_RED, false);
      break;
    case 0xc32: 
      ax_test_bjt_pnp(LEAD_BLUE, LEAD_RED, LEAD_GREEN, false);
      break;    
    case 0x38c: 
      ax_test_bjt_pnp(LEAD_RED, LEAD_BLUE, LEAD_GREEN, false);
      break;
    case 0x32c: 
      ax_test_bjt_pnp(LEAD_RED, LEAD_GREEN, LEAD_BLUE, false);
      break;
    case 0x8c3: 
      ax_test_bjt_pnp(LEAD_GREEN, LEAD_RED, LEAD_BLUE, false);
      break;
    case 0x2c3: 
      ax_test_bjt_pnp(LEAD_GREEN, LEAD_BLUE, LEAD_RED, false);
      break;
    // Darlington NPN with FWD
    case 0x3cd: 
      ax_test_bjt_npn(LEAD_BLUE, LEAD_GREEN, LEAD_RED, true);
      break;
    case 0x3c7: 
      ax_test_bjt_npn(LEAD_BLUE, LEAD_RED, LEAD_GREEN, true);
      break;    
    case 0xcd3: 
      ax_test_bjt_npn(LEAD_RED, LEAD_BLUE, LEAD_GREEN, true);
      break;
    case 0xc73: 
      ax_test_bjt_npn(LEAD_RED, LEAD_GREEN, LEAD_BLUE, true);
      break;
    case 0xd3c: 
      ax_test_bjt_npn(LEAD_GREEN, LEAD_RED, LEAD_BLUE, true);
      break;
    case 0x73c: 
      ax_test_bjt_npn(LEAD_GREEN, LEAD_BLUE, LEAD_RED, true);
      break;
    // Darlington PNP with FWD
    case 0xc3b: 
      ax_test_bjt_pnp(LEAD_BLUE, LEAD_GREEN, LEAD_RED, true);
      break;
    case 0xc3e: 
      ax_test_bjt_pnp(LEAD_BLUE, LEAD_RED, LEAD_GREEN, true);
      break;    
    case 0x3bc: 
      ax_test_bjt_pnp(LEAD_RED, LEAD_BLUE, LEAD_GREEN, true);
      break;
    case 0x3ec: 
      ax_test_bjt_pnp(LEAD_RED, LEAD_GREEN, LEAD_BLUE, true);
      break;
    case 0xbc3: 
      ax_test_bjt_pnp(LEAD_GREEN, LEAD_RED, LEAD_BLUE, true);
      break;
    case 0xec3: 
      ax_test_bjt_pnp(LEAD_GREEN, LEAD_BLUE, LEAD_RED, true);
      break;
    // NCH Enh MOSFET, NCH Depl MOSFET, IGBT+FWD
    case 0x00d:
      ax_disc_mfetn_igbt(LEAD_BLUE, LEAD_GREEN, LEAD_RED);
      break;
    case 0x007:
      ax_disc_mfetn_igbt(LEAD_BLUE, LEAD_RED, LEAD_GREEN);
      break;
    case 0x0d0:
      ax_disc_mfetn_igbt(LEAD_RED, LEAD_BLUE, LEAD_GREEN);
      break;
    case 0x070:
      ax_disc_mfetn_igbt(LEAD_RED, LEAD_GREEN, LEAD_BLUE);
      break;
    case 0xd00:
      ax_disc_mfetn_igbt(LEAD_GREEN, LEAD_RED, LEAD_BLUE);
      break;
    case 0x700:
      ax_disc_mfetn_igbt(LEAD_GREEN, LEAD_BLUE, LEAD_RED);
      break;     
    // PCH Enh MOSFET
    case 0x00e:
      ax_test_mosfet_enhancement(LEAD_BLUE, LEAD_RED, LEAD_GREEN, true);
      break;
    case 0x00b:
      ax_test_mosfet_enhancement(LEAD_BLUE, LEAD_GREEN, LEAD_RED, true);
      break;
    case 0x0e0:
      ax_test_mosfet_enhancement(LEAD_RED, LEAD_GREEN, LEAD_BLUE, true);
      break;
    case 0x0b0:
      ax_test_mosfet_enhancement(LEAD_RED, LEAD_BLUE, LEAD_GREEN, true);
      break;
    case 0xe00:
      ax_test_mosfet_enhancement(LEAD_GREEN, LEAD_BLUE, LEAD_RED, true);
      break;
    case 0xb00:
      ax_test_mosfet_enhancement(LEAD_GREEN, LEAD_RED, LEAD_BLUE, true);
      break;
    // IGBT No FWD
    case 0x001:
      ax_test_igbt(LEAD_BLUE, LEAD_GREEN, LEAD_RED, false);
      break;
     case 0x004:
      ax_test_igbt(LEAD_BLUE, LEAD_RED, LEAD_GREEN, false);
      break;     
    case 0x010:
      ax_test_igbt(LEAD_RED, LEAD_BLUE, LEAD_GREEN, false);
      break;
     case 0x040:
      ax_test_igbt(LEAD_RED, LEAD_GREEN, LEAD_BLUE, false);
      break;     
    case 0x100:
      ax_test_igbt(LEAD_GREEN, LEAD_RED, LEAD_BLUE, false);
      break;
    case 0x400:
      ax_test_igbt(LEAD_GREEN, LEAD_BLUE, LEAD_RED, false);
      break; 
    // Thyristor
    case 0x0c1:
      ax_test_thyristor(LEAD_RED, LEAD_GREEN, LEAD_BLUE);
      break;
    case 0x304:
      ax_test_thyristor(LEAD_GREEN, LEAD_RED, LEAD_BLUE);
      break;     
    case 0xc10:
      ax_test_thyristor(LEAD_GREEN, LEAD_BLUE, LEAD_RED);
      break;
    case 0x043:
      ax_test_thyristor(LEAD_BLUE, LEAD_GREEN, LEAD_RED);
      break;  
    case 0x10c:
      ax_test_thyristor(LEAD_BLUE, LEAD_RED, LEAD_GREEN);
      break;
    case 0x430:
      ax_test_thyristor(LEAD_RED, LEAD_BLUE, LEAD_GREEN);
      break;   
    // Unknown or Bad Part
    default:
      cli_printf("\t-> Unknown or Bad Part");
      screen.screen = &scrn_error;
      screen.string1_override = "Unknown or Bad Part";
      screen.string2_override = "";
      scrn_draw(&screen);
      beep(C5, BEEP_LONG);
      break;
  }
}

/* Get ID flags for a particular lead configuration
 * Measure conduction between one pair of leads with the third lead biased positive or negative
 * Returns identity bits:
 *
 *    -ve +ve  
 *    [ ] [ ]  
 *    MSB LSB
 */
uint8_t ax_get_id(lead_t lead_from, lead_t lead_to, lead_t lead_cntrl)
{
  int32_t i_dut_p;
  int32_t i_dut_n;
  uint8_t result = 0;

  i_dut_p = ax_get_i_posi_drive(lead_from, lead_to, lead_cntrl, true);
  if(i_dut_p > AX_ID_LOW_I) { result |= AX_FLAG_COND_P; }
  
  i_dut_n = ax_get_i_negi_drive(lead_from, lead_to, lead_cntrl, true);
  if(i_dut_n > AX_ID_LOW_I) { result |= AX_FLAG_COND_N ; }

  #ifdef AX_VERBOSE
    cli_printf("\t*  %s -> %s %6i %6i %6i ", ax_lead_2_text(lead_from), ax_lead_2_text(lead_to), i_dut_p, i_dut_n, result);
  #endif
  return result;
}

/* Identity check
 * Measure conduction between each pair of leads with the third lead biased positive or negative
 * Returns identity flags:
 *
 *      r->b      b->r      b->g      g->b      g->r      r->g
 *    -ve +ve   -ve +ve   -ve +ve   -ve +ve   -ve +ve   -ve +ve
 *    [ ] [ ]   [ ] [ ]   [ ] [ ]   [ ] [ ]   [ ] [ ]   [ ] [ ]
 *    MSB                                                   LSB
 */
uint16_t ax_identity_check(void)
{
  uint16_t flags = 0;

  // r->g
  flags |= (ax_get_id(LEAD_RED, LEAD_GREEN, LEAD_BLUE) << 0);
  // g->r
  flags |= (ax_get_id(LEAD_GREEN, LEAD_RED, LEAD_BLUE) << 2);
  // g->b 
  flags |= (ax_get_id(LEAD_GREEN, LEAD_BLUE, LEAD_RED) << 4);
  // b->g
  flags |= (ax_get_id(LEAD_BLUE, LEAD_GREEN, LEAD_RED) << 6);
  // b->r
  flags |= (ax_get_id(LEAD_BLUE, LEAD_RED, LEAD_GREEN) << 8);
  // r->b
  flags |= (ax_get_id(LEAD_RED, LEAD_BLUE, LEAD_GREEN) << 10);

  return(flags);
}

/* Discriminate between BJT NPN and JFET N-CH
 * If the former, work out which lead is emitter and which is collector
 * 
 * Test 1: Check conduction with gate/base shorted one of the other leads - J-FET will conduct
 * and BJT will block. If J-FET, assign drain and source arbitraily since they are identical
 * 
 * Test 2: Check forward and reverse conduction with positive gate bias. Forward gain (emitter
 * negative) will be higher than reverse gain (collector negative) and higher than 5x
 */
void ax_disc_bjtn_jfetn(lead_t base_gate, lead_t lead_1, lead_t lead_2)
{
  int32_t i_dut_f;
  int32_t i_dut_r;
  screen_desc_t screen;

  cli_printf("\tBJT NPN or JFET N-CH?");

  // Test 1
  i_dut_f = ax_get_i_gnd_drive(lead_1, lead_2, base_gate, false);
  cli_printf("\t* Idut(0) = %i µA", i_dut_f);

  if(i_dut_f > AX_ID_MED_I) {    // JFET N-Ch
    ax_test_jfet(base_gate, lead_1, lead_2, false);
  }
  else {                          // BJT NPN, now find emiiter/collector
    cli_printf("\tBJT NPN find Emitter/Collector");
    // Test 2
    i_dut_f = ax_get_i_posi_drive(lead_1, lead_2, base_gate, false);
    i_dut_r = ax_get_i_posi_drive(lead_2, lead_1, base_gate, false);
    cli_printf("\t* Idut(1-2) = %i µA, Idut(2-1) = %i µA", i_dut_f, i_dut_r);

    if((i_dut_f > i_dut_r) &&
      (i_dut_f > 5 * AX_ID_DRIVE_I)){
      ax_test_bjt_npn(base_gate, lead_2, lead_1, false);
    }
    else if((i_dut_r > i_dut_f) &&
      (i_dut_r > 5 * AX_ID_DRIVE_I)){
      ax_test_bjt_npn(base_gate, lead_1, lead_2, false);
    }
    else {
    cli_printf("\t-> Unknown or Bad Part");
    screen.screen = &scrn_error;
    screen.string1_override = "Unknown or Bad Part";
    screen.string2_override = "";
    scrn_draw(&screen);
    }
  }
}

/* Discriminate between BJT PNP and JFET P-CH
 * If the former, work out which lead is emitter and which is collector
 * 
 * Test 1: Check conduction with gate/base shorted one of the other leads - J-FET will conduct
 * and BJT will block. If J-FET, assign drain and source arbitraily since they are identical
 * 
 * Test 2: Check forward and reverse conduction with positive gate bias. Forward gain (emitter
 * positive) will be higher than reverse gain (collector positive) and higher than 5x
 */
void ax_disc_bjtp_jfetp(lead_t base_gate, lead_t lead_1, lead_t lead_2)
{
  int32_t i_dut_f;
  int32_t i_dut_r;
  screen_desc_t screen;

  cli_printf("\tBJT PNP or JFET P-CH?");

  i_dut_f = ax_get_i_vplus_drive(lead_1, lead_2, base_gate, false);
  cli_printf("\t* Idut(0) = %i µA", i_dut_f);

  if(i_dut_f > AX_ID_MED_I) {    // JFET P-Ch
    ax_test_jfet(base_gate, lead_1, lead_2, true);
  }
  else {                          // BJT PNP, now find emiiter/collector
    cli_printf("\tBJT PNP find Emitter/Collector");
    // Test 2
    i_dut_f = ax_get_i_negi_drive(lead_1, lead_2, base_gate, false);
    i_dut_r = ax_get_i_negi_drive(lead_2, lead_1, base_gate, false);
    cli_printf("\t* Idut(1-2) = %i µA, Idut(2-1) = %i µA", i_dut_f, i_dut_r);

    if((i_dut_f > i_dut_r) &&
      (i_dut_f > 5 * AX_ID_DRIVE_I)){
      ax_test_bjt_pnp(base_gate, lead_1, lead_2, false);
    }
    else if((i_dut_r > i_dut_f) &&
      (i_dut_r > 5 * AX_ID_DRIVE_I)){
      ax_test_bjt_pnp(base_gate, lead_2, lead_1, false);
    }
    else {
    cli_printf("\t-> Unknown or Bad Part");
    screen.screen = &scrn_error;
    screen.string1_override = "Unknown or Bad Part";
    screen.string2_override = "";
    scrn_draw(&screen);
    }
  }
}

/* Discriminate between MOSFET Enh N-Ch, MOSFET Depl N-Ch and IGBT with freewheel diode
 * In each case we know which lead is which already
 * 
 * Test 1: Check conduction with gate/base shorted one of the other leads - Depletion MOSFET will 
 * conduct and the others will block. 
 * 
 * Test 2: Check voltage drop across reverse-biased DUT when driven with positive gate drive. For a
 * MOSFET the channel will short the body diode (which is forward biased) and the voltage will be 
 * less than 0.4 V. For an IGBT the voltage will be the forward-biased freewheel diode ( more than
 * 0.4 V but less than 2.0V )
 */
void ax_disc_mfetn_igbt(lead_t gate, lead_t src_em, lead_t drn_col)
{
  int32_t i_dut;
  int32_t v_dut;
  screen_desc_t screen;

  cli_printf("\tMOSFET Enh N-Ch, MOSFET Depl N-Ch or IGBT + FWD?");
  // Test 1
  i_dut = ax_get_i_gnd_drive(drn_col, src_em, gate,true);
  cli_printf("\t* Idut(0) = %i µA", i_dut);
  if(i_dut > AX_ID_VLOW_I) {        // Depletion MOSFET
    ax_test_mosfet_depletion(gate, src_em, drn_col);
  }
  else {                              // Enhancement MOSFET or IGBT + FWD
    cli_printf("\tMOSFET Enh N-Ch or IGBT + FWD?");
    // Test 2
    v_dut = ax_get_v_posv_drive(src_em, drn_col, gate, false);
    cli_printf("\t* Vdut(r) = %i mV", v_dut);
    if(v_dut < AX_ID_DIODE_V) {        // MOSFET
      ax_test_mosfet_enhancement(gate, src_em, drn_col, false);
    }
    else if((v_dut >= AX_ID_DIODE_V) &&
    (v_dut < 5 * AX_ID_DIODE_V)) {      // IGBT
      ax_test_igbt(gate, src_em, drn_col, true);
    }
    else {
    cli_printf("\t-> Unknown or Bad Part");
    screen.screen = &scrn_error;
    screen.string1_override = "Unknown or Bad Part";
    screen.string2_override = "";
    scrn_draw(&screen);
    }
  }
}

/* Discriminate between a Thyristor and a common-cathode diode pair 
 * We know the thyristor cathode or diode pair common cathode, but not the identity of the other
 * leads. 
 * 
 * Test 1: Check the conduction between leads 1 and 2 and the cathode/common cathode.  Both will 
 * conduct for the diode pair. The anodes are assigned arbitrarily because they are identical. 
 * For the thyristor, only the gate-cathode will conduct so we can identify the gate and therefore 
 * the anode.
 */
void ax_disc_diode_kk_thyristor(lead_t cathode_kk, lead_t lead_1, lead_t lead_2)
{
  int32_t i_dut_1;
  int32_t i_dut_2;
  screen_desc_t screen;

  cli_printf("\tDiode Pair KK or Thyristor?");

  i_dut_1 = ax_get_i_open_drive(lead_1, cathode_kk, lead_2, false);
  cli_printf("\t* Idut(1-2) = %i µA", i_dut_1);
  i_dut_2 = ax_get_i_open_drive(lead_2, cathode_kk, lead_1, false);
  cli_printf("\t* Idut(2-1) = %i µA", i_dut_2);

  if((i_dut_1 > AX_ID_HI_I) &&
     (i_dut_2 > AX_ID_HI_I)) {
    ax_test_diode_kk(lead_1, cathode_kk, lead_2);
    }
  else if ((i_dut_1 > AX_ID_HI_I) &&
           (i_dut_2 <= AX_ID_HI_I)) {
            // lead 1 is gate
    ax_test_thyristor(lead_2, cathode_kk, lead_1);
  }
  else if ((i_dut_1 <= AX_ID_HI_I) &&
           (i_dut_2 > AX_ID_HI_I)) {
            // lead 2 is gate
    ax_test_thyristor(lead_1, cathode_kk, lead_2);
  }
  else {
    cli_printf("\t-> Unknown or Bad Part");
    screen.screen = &scrn_error;
    screen.string1_override = "Unknown or Bad Part";
    screen.string2_override = "";
    scrn_draw(&screen);
  }
}
/* Discriminate between a Triac and something else
 * We know M2 but not the other two leads. It may not be a triac
 * 
 * Test 1: Test conduction from M2 to the other two leads with the third lead biased on at 2mA. 
 * Both should conduct but the current is a bit higher when the gate is driven vs M1
 */
bool ax_is_triac(lead_t gate_m1, lead_t m1_gate, lead_t m2)
{
  int32_t i_dut_1;
  int32_t i_dut_2;
  bool triac = false;


  cli_printf("\tTriac?");
  // Test 1
  an_set_load_mv(AX_TEST_V_HI - AX_TEST_V_LO );
  an_set_power(true);
  an_set_drive_ua(2000);

  an_set_matrix(NODE_POWER, m2);
  an_set_matrix(NODE_LOAD, m1_gate);
  an_set_matrix(NODE_DRIVE, gate_m1);
  i_dut_1 = -an_get_load_ua();
  an_set_matrix_all_open();

  an_set_matrix(NODE_POWER, m2);
  an_set_matrix(NODE_LOAD, gate_m1);
  an_set_matrix(NODE_DRIVE, m1_gate);
  i_dut_2 = -an_get_load_ua();
  an_set_matrix_all_open();

  cli_printf("\t* Idut(g/m1 driven) = %i µA, Idur(m1/g driven) = %i µA", i_dut_1, i_dut_2);

  if((i_dut_1 > AX_ID_MED_I) &&
      (i_dut_2 <  i_dut_1)) {
    ax_test_triac(m2, m1_gate, gate_m1);
    triac = true;
  }
  else if((i_dut_2 > AX_ID_MED_I) &&
      (i_dut_2 > i_dut_1)) {
    ax_test_triac(m2, gate_m1, m1_gate);
    triac = true;
  }
  return(triac);
}

/* Detect a short circuit 
 * Measure voltage drop across DUT with the drive lead open. If both are elss than 100mV we call 
 * this a short. 
 */
bool ax_is_short(lead_t lead_1, lead_t lead_2)
{
  int32_t v_dut_1;
  int32_t v_dut_2;

  v_dut_1 = ax_get_v_open_drive(lead_1, lead_2, LEAD_NONE,  true);
  v_dut_2 = ax_get_v_open_drive(lead_2, lead_1, LEAD_NONE,  true);

  if((v_dut_1 < AX_ID_SHORT_V) &&
     (v_dut_2 < AX_ID_SHORT_V) ) {
    return(true);
  }
  return(false);
}
/* End analyser */