
Knee flexion axes optimisation code based on the method proposed by Baker et al. (1999) and then slightly modified by Schache et al. (2006).



{*Start of macro section*}
{*======================*}

{*-----------------------------------------------------------------------------------------------*}
{*Knee optimisation macros based on code by Baker 1999 and Schache 2005*}
{*-----------------------------------------------------------------------------------------------*}
macro FemurRotate(Femur, Tibia, deg, var, ThetaMin)
	Femur = ROT(Femur, Femur(2), deg)
	KneeA = <Femur, Tibia,zxy>	
	ValAve=AVERAGE(KneeA(2))
	ValVar=AVERAGE((KneeA(2)-ValAve)*(KneeA(2)-ValAve))
	var=ValVar
	
	{*Calculate theta min*}
	AveF=AVERAGE(sin(KneeA(1)))
	FleVar=AVERAGE((sin(KneeA(1))-AveF)*(sin(KneeA(1))-AveF))
	Cov=AVERAGE((KneeA(2)-ValAve)*(sin(KneeA(1))-AveF))
	ThetaMin=Cov/FleVar
endmacro

macro SUBSTITUTE4(p1,p2,p3,p4)
{*Replaces any point missing from set of four fixed in a segment*}
s234 = [p3,p2-p3,p3-p4]
p1V = Average(p1/s234)*s234
s341 = [p4,p3-p4,p4-p1]
p2V = Average(p2/s341)*s341
s412 = [p1,p4-p1,p1-p2]
p3V = Average(p3/s412)*s412
s123 = [p2,p1-p2,p2-p3]
p4V = Average(p4/s123)*s123

p1 = (p1+p1V)/2 ? p1 ? p1V
p2 = (p2+p2V)/2 ? p2 ? p2V
p3 = (p3+p3V)/2 ? p3 ? p3V
p4 = (p4+p4V)/2 ? p4 ? p4V

endmacro

Macro DYNPOINTER(AnatPoint,Segment)
{* Creates GLOBAL ALs using calibration points recorded during static
trials into dynamic trials*}
  IF EXIST(%#AnatPoint#Calib)		
    AnatPoint=%#AnatPoint#Calib*Segment
    OUTPUT(AnatPoint)
    PARAM(AnatPoint)
  ENDIF 
EndMacro

macro SEGVIS(Segment)
{*outputs a visual representaion of the segment to be viewed in the Workspace*}
{*0(Segment) is the origin of the segment*}
ORIGIN#Segment=0(Segment)
XAXIS#Segment=0(Segment)+(1(Segment)*100)
YAXIS#Segment=0(Segment)+(2(Segment)*100)
ZAXIS#Segment=0(Segment)+(3(Segment)*100)
OUTPUT(ORIGIN#Segment,XAXIS#Segment,YAXIS#Segment,ZAXIS#Segment)
endmacro

{*End of macro section*}

{* Anthropometric Data: From DA Winter, Biomechanics and Motor Control of Human Movement *}

AnthropometricData
DefaultPelvis 0.142 0.895 0.5 0.3
DefaultFemur 0.1 0.567 0.323 0
DefaultTibia 0.0465 0.567 0.302 0
DefaultFoot 0.0145 0.5 0.475 0
EndAnthropometricData


{*Optional points are points which may not be present in every trial*}

OptionalPoints(Pointer1,Pointer2)

OptionalPoints(WAIST1,WAIST2,WAIST3,WAIST4)
OptionalPoints(RTH1,RTH2,RTH3,RTH4)
OptionalPoints(LTH1,LTH2,LTH3,LTH4)
OptionalPoints(RSH1,RSH2,RSH3,RSH4)
OptionalPoints(LSH1,LSH2,LSH3,LSH4)
OptionalPoints(RMET1,RMET5,RHEEL,LMET1,LMET5,LHEEL)
OptionalPoints(RHJC,LHJC,RKJC,LKJC,RAJC,LAJC)
OptionalPoints(%RASIS, %LASIS, %RPSIS,%LPSIS)
OptionalPoints(%RMEPI, %RLEPI, %LMEPI, %LLEPI)
OptionalPoints(%LLMAL,%LMMAL,%RLMAL,%RMMAL)
OptionalPoints(RASIS,LASIS,RPSIS,LPSIS,PELF)
OptionalPoints(RLMAL,RMMAL,LLMAL,LMMAL)
OptionalPoints(RMEPI,RLEPI,LMEPI,LLEPI)

{*Substitutes missing markers based on clusters of 4 markers*}
SUBSTITUTE4(WAIST1,WAIST2,WAIST3,WAIST4)
SUBSTITUTE4(RTH1,RTH2,RTH3,RTH4)			
SUBSTITUTE4(LTH1,LTH2,LTH3,LTH4)
SUBSTITUTE4(RSH1,RSH2,RSH3,RSH4)
SUBSTITUTE4(LSH1,LSH2,LSH3,LSH4)

{*Marker cluster axis definitions*}
{*Defines technical axis systems for the segments from the marker on the clusters*}

WaistSeg=[WAIST1,WAIST1-WAIST3,WAIST3-WAIST2,zyx]
RThighSeg=[RTH1,RTH1-RTH3,RTH3-RTH2,zyx]
RShinSeg=[RSH1,RSH1-RSH4,RSH2-RSH4,zxy]  
LThighSeg=[LTH1,LTH1-LTH3,LTH3-LTH2,zyx]
LShinSeg=[LSH1,LSH1-LSH4,LSH2-LSH4,zxy]  

SEGVIS(WaistSeg)
SEGVIS(RThighSeg)
SEGVIS(LThighSeg)
SEGVIS(RShinSeg)
SEGVIS(LShinSeg)


{*Dynamic Trials*}

  {*Anatomical frame definition*}

RLEPI=%RLEPI*RThighSeg
RMEPI=%RMEPI*RThighSeg
LLEPI=%LLEPI*LThighSeg
LMEPI=%LMEPI*LThighSeg

RASIS=%RASIS*WaistSeg
LASIS=%LASIS*WaistSeg
RPSIS=%RPSIS*WaistSeg
LPSIS=%LPSIS*WaistSeg

RMMAL=%RMMAL*RShinSeg
RLMAL=%RLMAL*RShinSeg
LMMAL=%LMMAL*LShinSeg
LLMAL=%LLMAL*LShinSeg

  {*Segment definitions*}
    
Global=[{0,0,0},{0,-1,0},{0,0,1},zxy]
SEGVIS(Global)
  {*Pelvis Segment*}
      {*HJC calculated using equations from Harrington2007*}
{*Hip joint centre is a function of interAsis Distance and PelvicDepth*}

SACR=(LPSIS+RPSIS)/2
OUTPUT(SACR)
PARAM(SACR)

PELF=(LASIS+RASIS)/2
OUTPUT(PELF)
PARAM(PELF)

Pelvis=[PELF, RASIS-LASIS, SACR-PELF, zyx]

InterASISDist=$InterASISDist
OUTPUT(InterASISDist)

XHJC=(-0.24*$PelvicDepth)-9.9
YHJC=(-0.3*InterASISDist)-10.9
ZHJC=(0.33*InterASISDist)+7.3

RHJC = {XHJC,YHJC,ZHJC}*Pelvis
LHJC = {XHJC,YHJC,-ZHJC}*Pelvis
Pelvis = (LHJC+RHJC)/2 + Attitude(Pelvis)
SEGVIS(Pelvis)
OUTPUT (RHJC, LHJC)
PARAM (RHJC, LHJC)

RHipSeg=[RHJC, RASIS-LASIS, SACR-PELF, zyx]
LHipSeg=[LHJC, RASIS-LASIS, SACR-PELF, zyx]
SEGVIS(RHipSeg)
SEGVIS(LHipSeg)

   {*Right Thigh Segment*}
   {*Define standard thigh segment*}
RKJC=(RLEPI+RMEPI)/2
OUTPUT(RKJC)
PARAM(RKJC)
RFemur=[RKJC, RHJC-RKJC, RMEPI-RLEPI, yxz]
SEGVIS(RFemur)

   {*Left Thigh Segment*}
LKJC=(LLEPI+LMEPI)/2
OUTPUT(LKJC)
PARAM(LKJC)
LFemur=[LKJC, LHJC-LKJC, LLEPI-LMEPI, yxz]
SEGVIS(LFemur) 

   {*Right Shin System*}
RAJC=(RMMAL+RLMAL)/2
OUTPUT(RAJC)
PARAM(RAJC)
RTibia=[RAJC, RKJC-RAJC, RMMAL-RLMAL, yxz]
SEGVIS(RTibia)

  {*Left Shin System*}
LAJC=(LMMAL+LLMAL)/2
OUTPUT(LAJC)
PARAM(LAJC)
LTibia=[LAJC, LKJC-LAJC, LLMAL-LMMAL, yxz]
SEGVIS(LTibia) 

    {*Foot System*}
    {*Consider this to represent the shoe rather than the foot. The markers are put on 
      so they lie in a plane parallel to the floor in the neutral
      postion and this can be considered as ankle joint neutral. *}
   
  {*Right Foot System*}
RmidFOOT=(RMET1+RMET5)/2
RFootSeg=[RHEEL, RHEEL-RmidFOOT, RMET1-RMET5, yxz]
OUTPUT(RmidFOOT)
SEGVIS(RFootSeg)

  {*Left Foot System*}
LmidFOOT=(LMET1+LMET5)/2
LFootSeg=[LHEEL, LHEEL-LmidFOOT, LMET5-LMET1, yxz]
OUTPUT(LmidFOOT)
SEGVIS(LFootSeg)


{*Optimise the Left Knee*}
{* Determination of offset leading to minimum variance is iterative process.	*}

GuessLo=-30		
   GuessHi= 30
		
		LFemur=[LKJC,LHJC-LKJC,LLEPI-LMEPI,yxz]
		FemurRotate(LFemur, LTibia, GuessLo, VarLo, $LThetaMin)
		
		LFemur=[LKJC,LHJC-LKJC,LLEPI-LMEPI,yxz]
		FemurRotate(LFemur, LTibia, GuessHi, VarHi, $LThetaMin)
				
		{*Guess 512*}
			IF VarHi>VarLo THEN
				GuessHi=(GuessLo+GuessHi)/2
			ELSE
				GuessLo=(GuessLo+GuessHi)/2
			ENDIF
			LFemur=[LKJC,LHJC-LKJC,LLEPI-LMEPI,yxz]
			FemurRotate(LFemur, LTibia, GuessLo, VarLo, $LThetaMin)
			LFemur=[LKJC,LHJC-LKJC,LLEPI-LMEPI,yxz]
			FemurRotate(LFemur, LTibia, GuessHi, VarHi, $LThetaMin)
		
		{*Guess 256*}
			IF VarHi>VarLo THEN
				GuessHi=(GuessLo+GuessHi)/2
			ELSE
				GuessLo=(GuessLo+GuessHi)/2
			ENDIF
			LFemur=[LKJC,LHJC-LKJC,LLEPI-LMEPI,yxz]
			FemurRotate(LFemur, LTibia, GuessLo, VarLo, $LThetaMin)
			LFemur=[LKJC,LHJC-LKJC,LLEPI-LMEPI,yxz]
			FemurRotate(LFemur, LTibia, GuessHi, VarHi, $LThetaMin)
		
               {*Guess 128*}
			IF VarHi>VarLo THEN
				GuessHi=(GuessLo+GuessHi)/2
			ELSE
				GuessLo=(GuessLo+GuessHi)/2
			ENDIF
			LFemur=[LKJC,LHJC-LKJC,LLEPI-LMEPI,yxz]
			FemurRotate(LFemur, LTibia, GuessLo, VarLo, $LThetaMin)
			LFemur=[LKJC,LHJC-LKJC,LLEPI-LMEPI,yxz]
			FemurRotate(LFemur, LTibia, GuessHi, VarHi, $LThetaMin)
            
              {*Guess 64*}
			IF VarHi>VarLo THEN
				GuessHi=(GuessLo+GuessHi)/2
			ELSE
				GuessLo=(GuessLo+GuessHi)/2
			ENDIF
			LFemur=[LKJC,LHJC-LKJC,LLEPI-LMEPI,yxz]
			FemurRotate(LFemur, LTibia, GuessLo, VarLo, $LThetaMin)
			LFemur=[LKJC,LHJC-LKJC,LLEPI-LMEPI,yxz]
			FemurRotate(LFemur, LTibia, GuessHi, VarHi, $LThetaMin)   
            
              {*Guess 32*}
			IF VarHi>VarLo THEN
				GuessHi=(GuessLo+GuessHi)/2
			ELSE
				GuessLo=(GuessLo+GuessHi)/2
			ENDIF
			LFemur=[LKJC,LHJC-LKJC,LLEPI-LMEPI,yxz]
			FemurRotate(LFemur, LTibia, GuessLo, VarLo, $LThetaMin)
			LFemur=[LKJC,LHJC-LKJC,LLEPI-LMEPI,yxz]
			FemurRotate(LFemur, LTibia, GuessHi, VarHi, $LThetaMin)    
        
              {*Guess 16*}
			IF VarHi>VarLo THEN
				GuessHi=(GuessLo+GuessHi)/2
			ELSE
				GuessLo=(GuessLo+GuessHi)/2
			ENDIF
			LLFemur=[LKJC,LHJC-LKJC,LLEPI-LMEPI,yxz]
			FemurRotate(LFemur, LTibia, GuessLo, VarLo, $LThetaMin)
			LFemur=[LKJC,LHJC-LKJC,LLEPI-LMEPI,yxz]
			FemurRotate(LFemur, LTibia, GuessHi, VarHi, $LThetaMin)  

               {*Guess 8*}
			IF VarHi>VarLo THEN
				GuessHi=(GuessLo+GuessHi)/2
			ELSE
				GuessLo=(GuessLo+GuessHi)/2
			ENDIF
			LFemur=[LKJC,LHJC-LKJC,LLEPI-LMEPI,yxz]
			FemurRotate(LFemur, LTibia, GuessLo, VarLo, $LThetaMin)
			LFemur=[LKJC,LHJC-LKJC,LLEPI-LMEPI,yxz]
			FemurRotate(LFemur, LTibia, GuessHi, VarHi, $LThetaMin)
            
              {*Guess 4*}
			IF VarHi>VarLo THEN
				GuessHi=(GuessLo+GuessHi)/2
			ELSE
				GuessLo=(GuessLo+GuessHi)/2
			ENDIF
			LFemur=[LKJC,LHJC-LKJC,LLEPI-LMEPI,yxz]
			FemurRotate(LFemur, LTibia, GuessLo, VarLo, $LThetaMin)
			LFemur=[LKJC,LHJC-LKJC,LLEPI-LMEPI,yxz]
			FemurRotate(LFemur, LTibia, GuessHi, VarHi, $LThetaMin)
            
              {*Guess 2*}
			IF VarHi>VarLo THEN
				GuessHi=(GuessLo+GuessHi)/2
			ELSE
				GuessLo=(GuessLo+GuessHi)/2
			ENDIF
			LFemur=[LKJC,LHJC-LKJC,LLEPI-LMEPI,yxz]
			FemurRotate(LFemur, LTibia, GuessLo, VarLo, $LThetaMin)
			LFemur=[LKJC,LHJC-LKJC,LLEPI-LMEPI,yxz]
			FemurRotate(LFemur, LTibia, GuessHi, VarHi, $LThetaMin)
            
              {*Guess 1*}
			IF VarHi>VarLo THEN
				GuessHi=(GuessLo+GuessHi)/2
			ELSE
				GuessLo=(GuessLo+GuessHi)/2
			ENDIF
			LFemur=[LKJC,LHJC-LKJC,LLEPI-LMEPI,yxz]
			FemurRotate(LFemur, LTibia, GuessLo, VarLo, $LThetaMin)
			LFemur=[LKJC,LHJC-LKJC,LLEPI-LMEPI,yxz]
			FemurRotate(LFemur, LTibia, GuessHi, VarHi, $LThetaMin)
        
                {*Apply final guess rotation and set output parameters*}
			$LFemurRot=(GuessHi+GuessLo)/2
			LFemur=[LKJC,LHJC-LKJC,LLEPI-LMEPI,yxz]
			FemurRotate(LFemur, LTibia, $LFemurRot, $LValVar, $LThetaMin)
			
		{*Baker's quality checks*}
                {* Add 360deg to output correction if quality checks are failed*} 
			IF ($LValVar>9) or ($LValVar<-9) or ($LThetaMin>1) or ($LThetaMin < -1)
				$LFemurRot =360 + $LFemurRot
			ENDIF

		{*Output parameters*}
			PARAM($LFemurRot,$LThetaMin,$LValVar)


{*Optimise the Right Knee*}
 GuessLo=-30	
    GuessHi= 30
        
		RFemur=[RKJC, RHJC-RKJC, RMEPI-RLEPI, yxz]
		FemurRotate(RFemur, RTibia, GuessLo, VarLo, $RThetaMin)
		
		RFemur=[RKJC, RHJC-RKJC, RMEPI-RLEPI, yxz]
		FemurRotate(RFemur, RTibia, GuessHi, VarHi, $RThetaMin)
				
        {*Guess 512*}
			IF VarHi>VarLo THEN
				GuessHi=(GuessLo+GuessHi)/2
			ELSE
				GuessLo=(GuessLo+GuessHi)/2
			ENDIF
			RFemur=[RKJC, RHJC-RKJC, RMEPI-RLEPI, yxz]
			FemurRotate(RFemur, RTibia, GuessLo, VarLo, $RThetaMin)
			RFemur=[RKJC, RHJC-RKJC, RMEPI-RLEPI, yxz]
			FemurRotate(RFemur, RTibia, GuessHi, VarHi, $RThetaMin)
		
        {*Guess 256*}
			IF VarHi>VarLo THEN
				GuessHi=(GuessLo+GuessHi)/2
			ELSE
				GuessLo=(GuessLo+GuessHi)/2
			ENDIF
			RFemur=[RKJC, RHJC-RKJC, RMEPI-RLEPI, yxz]
			FemurRotate(RFemur, RTibia, GuessLo, VarLo, $RThetaMin)
			RFemur=[RKJC, RHJC-RKJC, RMEPI-RLEPI, yxz]
			FemurRotate(RFemur, RTibia, GuessHi, VarHi, $RThetaMin)
            
        {*Guess 128*}
			IF VarHi>VarLo THEN
				GuessHi=(GuessLo+GuessHi)/2
			ELSE
				GuessLo=(GuessLo+GuessHi)/2
			ENDIF
			RFemur=[RKJC, RHJC-RKJC, RMEPI-RLEPI, yxz]
			FemurRotate(RFemur, RTibia, GuessLo, VarLo, $RThetaMin)
			RFemur=[RKJC, RHJC-RKJC, RMEPI-RLEPI, yxz]
			FemurRotate(RFemur, RTibia, GuessHi, VarHi, $RThetaMin)
		
        {*Guess 64*}
			IF VarHi>VarLo THEN
				GuessHi=(GuessLo+GuessHi)/2
			ELSE
				GuessLo=(GuessLo+GuessHi)/2
			ENDIF
			RFemur=[RKJC, RHJC-RKJC, RMEPI-RLEPI, yxz]
			FemurRotate(RFemur, RTibia, GuessLo, VarLo, $RThetaMin)
			RFemur=[RKJC, RHJC-RKJC, RMEPI-RLEPI, yxz]
			FemurRotate(RFemur, RTibia, GuessHi, VarHi, $RThetaMin)
            
        {*Guess 32*}
			IF VarHi>VarLo THEN
				GuessHi=(GuessLo+GuessHi)/2
			ELSE
				GuessLo=(GuessLo+GuessHi)/2
			ENDIF
			RFemur=[RKJC, RHJC-RKJC, RMEPI-RLEPI, yxz]
			FemurRotate(RFemur, RTibia, GuessLo, VarLo, $RThetaMin)
			RFemur=[RKJC, RHJC-RKJC, RMEPI-RLEPI, yxz]
			FemurRotate(RFemur, RTibia, GuessHi, VarHi, $RThetaMin)

        {*Guess 16*}
			IF VarHi>VarLo THEN
				GuessHi=(GuessLo+GuessHi)/2
			ELSE
				GuessLo=(GuessLo+GuessHi)/2
			ENDIF
			RFemur=[RKJC, RHJC-RKJC, RMEPI-RLEPI, yxz]
			FemurRotate(RFemur, RTibia, GuessLo, VarLo, $RThetaMin)
			RFemur=[RKJC, RHJC-RKJC, RMEPI-RLEPI, yxz]
			FemurRotate(RFemur, RTibia, GuessHi, VarHi, $RThetaMin)

        {*Guess 8*}
			IF VarHi>VarLo THEN
				GuessHi=(GuessLo+GuessHi)/2
			ELSE
				GuessLo=(GuessLo+GuessHi)/2
			ENDIF
			RFemur=[RKJC, RHJC-RKJC, RMEPI-RLEPI, yxz]
			FemurRotate(RFemur, RTibia, GuessLo, VarLo, $RThetaMin)
			RFemur=[RKJC, RHJC-RKJC, RMEPI-RLEPI, yxz]
			FemurRotate(RFemur, RTibia, GuessHi, VarHi, $RThetaMin)
            
        {*Guess 4*}
			IF VarHi>VarLo THEN
				GuessHi=(GuessLo+GuessHi)/2
			ELSE
				GuessLo=(GuessLo+GuessHi)/2
			ENDIF
			RFemur=[RKJC, RHJC-RKJC, RMEPI-RLEPI, yxz]
			FemurRotate(RFemur, RTibia, GuessLo, VarLo, $RThetaMin)
			RFemur=[RKJC, RHJC-RKJC, RMEPI-RLEPI, yxz]
			FemurRotate(RFemur, RTibia, GuessHi, VarHi, $RThetaMin)
            
        {*Guess 2*}
			IF VarHi>VarLo THEN
				GuessHi=(GuessLo+GuessHi)/2
			ELSE
				GuessLo=(GuessLo+GuessHi)/2
			ENDIF
			RFemur=[RKJC, RHJC-RKJC, RMEPI-RLEPI, yxz]
			FemurRotate(RFemur, RTibia, GuessLo, VarLo, $RThetaMin)
			RFemur=[RKJC, RHJC-RKJC, RMEPI-RLEPI, yxz]
			FemurRotate(RFemur, RTibia, GuessHi, VarHi, $RThetaMin)

        {*Guess 1*}
			IF VarHi>VarLo THEN
				GuessHi=(GuessLo+GuessHi)/2
			ELSE
				GuessLo=(GuessLo+GuessHi)/2
			ENDIF
			RFemur=[RKJC, RHJC-RKJC, RMEPI-RLEPI, yxz]
			FemurRotate(RFemur, RTibia, GuessLo, VarLo, $RThetaMin)
			RFemur=[RKJC, RHJC-RKJC, RMEPI-RLEPI, yxz]
			FemurRotate(RFemur, RTibia, GuessHi, VarHi, $RThetaMin)
            
        {*Apply final guess rotation and set output parameters*}
			$RFemurRot=(GuessHi+GuessLo)/2
			RFemur=[RKJC, RHJC-RKJC, RMEPI-RLEPI, yxz]
			FemurRotate(RFemur, RTibia, $RFemurRot, $RValVar, $RThetaMin)
			
		{*Baker's quality checks*}
                {* Add 360deg to output correction if quality checks are failed*}  
			IF ($RValVar>9) or ($RValVar<-9) or ($RThetaMin>1) or ($RThetaMin < -1)
				$RFemurRot =360 + $RFemurRot
			ENDIF

		{*Output parameters*}
			PARAM($RFemurRot,$RThetaMin,$RValVar)
        
{*Ends dynamic trials*}


 
