Universal Control Remapper (UCR) - v0.1.22 28th Oct 2018

Post gaming related scripts
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

Re: Universal Control Remapper (UCR) - v0.1.19 26th Dec 2017

11 Jan 2018, 18:19

@StrikeEagle The deadzone and all other calculations upon the axis in UCR are part of the plugin, so if you wanted a deadzone for a non-centered axis, you could just use a modified version of the AxisToAxis plugin for that axis mapping. See the previous post I made where I altered the AxisToAxis plugin for another purpose.
You would also need to remove the old deadzone calculation code - just remove these lines from your copy of the AxisToAxis plugin

Code: Select all

			if (this.GuiControls.Deadzone.Get()){
				value := UCR.Libraries.StickOps.Deadzone(value, this.GuiControls.Deadzone.Get())
			}
Bear in mind that given the axis is 0..100 and say you want a 10% deadzone on that, you generally do not want to just set all values <10 to 0.
If you do, a value of 11 will pass through, and the axis would suddenly jump to 11% when it should be 0% at the end of the deadzone. You need to subtract the 10%, then re-scale any remainder to fit 100% of the axis scale.

So as you can see, multiple deadzones would also start to become pretty complex maths.
However, for single deadzones, I agree that there should be a feature to apply it to either end of the axis or the middle.

I am not sure if I can be bothered to update the code for this version of UCR to incorporate this, but it is certainly something I will consider for the new version.
Also, chaining plugins or sub-components (eg dynamically adding in x number of deadzones / inversions etc) is certainly something we are considering trying to do in the new version.
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

Re: Universal Control Remapper (UCR) - v0.1.19 26th Dec 2017

11 Jan 2018, 18:22

If the axes appear inverted, probably best to check the "invert" box in the plugin at run-time, and write the code to expect 0 = low end, 100 = high end.
So are you able to fiddle / tweak my code? You seem comfy enough with the maths. Don't worry about UI for the moment, let's get the code working first
Oliver
Posts: 27
Joined: 11 Jan 2018, 10:50

Re: Universal Control Remapper (UCR) - v0.1.19 26th Dec 2017

11 Jan 2018, 18:36

Test succeeded! With the percentages set on a scale from 0% to X% before the detent and Invert checked in the GUI it works as expected.

Only thing now would be to allow users to enter both percentages via the GUI so they can change it per profile and indeed scratch variables from the code that are not necessary in this scenario to keep the script neat and clean.
Last edited by Oliver on 12 Jan 2018, 02:34, edited 1 time in total.
Oliver
Posts: 27
Joined: 11 Jan 2018, 10:50

Re: Universal Control Remapper (UCR) - v0.1.19 26th Dec 2017

11 Jan 2018, 18:46

A little background info:

I don't know what you know about flightsims and stuff but the issue is DCS World. It doesn't have an option to set when a jet goes into afterburner. This differs per jet and is hardcoded by the developers of the specific jet at a point somewhere at X percent of throttle lever travel. Most flightsim hardware does have a physical detent. However, the percentage of travel required on the physical throttle to reach the detent almost never matches what the software developers set for the aircraft.

Using the above mechanism this would allow one to actually align the physical throttle's detent with the detent programmed in the flightsim. If you don't do this your only option would be to set curves in the game which will naturally result in non-linear throttle response both before the flightsim's detent and after.
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

Re: Universal Control Remapper (UCR) - v0.1.19 26th Dec 2017

12 Jan 2018, 06:32

Yup, totally familiar with the use-case, have come up against this issue myself.
Am thinking though that whatever solution we go with ideally should support multiple detent rescalings.
For example, my old Hotas X had an afterburner detent near the top and an idle detent near the bottom too. Some people may eg want the negative part of the scale to be below the lower detent and the AB to be on the upper detent.
We maybe need to look at Helgef's code and see if we can work that in - not sure how we could easily implement the UI to add multiple detent settings tho - it's the fact that you need two values to describe each detent point that is the problem.
Oliver
Posts: 27
Joined: 11 Jan 2018, 10:50

Re: Universal Control Remapper (UCR) - v0.1.19 26th Dec 2017

12 Jan 2018, 07:20

Well if you can enter the physical detent and the virtual detent you can just make profiles using your flexible GUI. I'd make a profile for the DCS M-2000C and the DCS F-5E-3. (And the Hornet soon.)
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

Re: Universal Control Remapper (UCR) - v0.1.19 26th Dec 2017

12 Jan 2018, 07:43

Here is a prototype that combines Helgef's multi-breakpoint code with a custom UI which allows adding/removing of breakpoints

Basically in order to save this "Setting", we use a hidden GuiControl containing a text string.
Any time you change the settings, the string is changed and gets stored in UCRs ini file.

So here is a test script that shows the text string that will be saved/loaded, plus a slider so you can simulate various input values.
If we can get this code behaving right, then this should basically cater for all scenarios

Code: Select all

#SingleInstance force

sc := new SegmentControl(Func("GuiControlCallback"))
setting := "0,0|60,80|100,100"

Gui, Add, Text, xm y+10, Hidden Settings Control
Gui, Add, Edit, w125 xm y+5 hwndhSettingsEdit, % setting

Gui, Add, Text, xm y+5, Input Simulation
Gui, Add, Slider, xm w125 y+5 gInputSliderChanged vSliderValue

sc.LoadSettings(setting)

Gui, Show, x0 y0
return

GuiClose:
	ExitApp

InputSliderChanged:
	Gui, Submit, NoHide
	ToolTip % SliderValue " = " sc.f(SliderValue)
	return

GuiControlCallback(value){
	global hSettingsEdit
	
	GuiControl, , % hSettingsEdit, % value
}

class SegmentControl {
	Segments := []
	Points := []
	
	__New(callback, options := ""){
		this.callback := callback
		Gui, Add, ListView, hwndhwnd w100 h100, Lower|Upper
		this.hLV := hwnd
		Gui, Add, Button, hwndhwnd x+5 yp w20 h100, -
		fn := this.DeleteClicked.Bind(this)
		GuiControl, +g, % hwnd, % fn
		Gui, Add, Edit, xm y+5 hwndhwnd w45
		this.hEditLow := hwnd
		Gui, Add, Edit, x+10 yp hwndhwnd w45
		this.hEditHigh := hwnd
		Gui, Add, Button, hwndhwnd x+5 yp w20, +
		fn := this.AddClicked.Bind(this)
		GuiControl, +g, % hwnd, % fn
	}
	
	AddPoint(x, y, save := true){
		this.Points.push([x, y])
		this.BuildSegments()
		if (save){
			this.SaveSettings()
		}
	}
	
	LoadSettings(str){
		chunks := StrSplit(str, "|")
		for i, pt in chunks {
			p := StrSplit(pt, ",")
			this.AddPoint(p[1], p[2], false)
		}
	}
	
	SaveSettings(){
		str := ""
		for i, chunk in this.Points {
			if (i > 1){
				str .= "|"
			}
			str .= chunk[1] "," chunk[2]
		}
		this.callback.Call(str)
	}
	
	BuildSegments(){
		Gui, ListView, % hLV
		LV_Delete()
		for i, p in this.Points {
			LV_Add(, p[1], p[2])
		}
		pts := this.Points.clone()
		p0 := pts.removeat(1)
		this.Segments := []
		for i, p1 in pts {
			this.Segments.push( {k : (p1.2-p0.2) / (p1.1-p0.1) , m : ( p1.1*p0.2 - p0.1*p1.2 ) / (p1.1-p0.1), end : p1.1 } ), p0 := p1
		}
	}
	
	AddClicked(){
		GuiControlGet, low, , % this.hEditLow
		GuiControlGet, high, , % this.hEditHigh
		if (this.IsNumeric(low) && this.IsNumeric(high)){
			this.AddPoint(low, high)
		}
	}
	
	DeleteClicked(){
		Gui, ListView, % hLV
		row := LV_GetNext()
		this.Points.RemoveAt(row)
		this.BuildSegments()
		this.SaveSettings()
	}
	
	IsNumeric(str){
		if str is number
			return true
		return false
	}
	
	f(x){
		for i, seg in this.Segments
			if (x < seg.end)
				return x*seg.k+seg.m
	}
}
I think that Helgef's code might need a bit of tweaking though? An input value of 100 gives an empty string for the output
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: Universal Control Remapper (UCR) - v0.1.19 26th Dec 2017

12 Jan 2018, 08:05

Hello. Please excuse my failure to understand what is going on, am I correct in that the idea is to move a real joystick and output a virtual joystick axis? I.e, you set the real joystick at 80 and get virtual at 70? If I recall correctly, UCR axes range from 0..50..100, where 0 is joystick at full deflection in one direction, 50 is center, and 100 is full deflection in the other direction. So, really, you need a graph looking like this,
line_.jpg
line_.jpg (50.01 KiB) Viewed 3557 times
Image credis: SVGraph.ahk.
Here, segs := segmentsFromPoints([0,0], [20,30], [50,50],[80,70], [100,100]). Center is [50,50]

Cheers.
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

Re: Universal Control Remapper (UCR) - v0.1.19 26th Dec 2017

12 Jan 2018, 08:13

Code: Select all

segs := segmentsFromPoints([0,0], [80,70], [100,110])
str := "99= " f(99,segs) ", 100= " f(100,segs)
clipboard := str

segmentsFromPoints(pts*){
	pts := pts.clone()
	p0 := pts.removeat(1)
	segs := []
	for i, p1 in pts
		segs.push( {k : (p1.2-p0.2) / (p1.1-p0.1) , m : ( p1.1*p0.2 - p0.1*p1.2 ) / (p1.1-p0.1), end : p1.1 } ), p0 := p1
	return segs
}
f(x, segs){
	for i, seg in segs
		if (x < seg.end)
			return x*seg.k+seg.m
}
Result:
99= 108.000000, 100=
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: Universal Control Remapper (UCR) - v0.1.19 26th Dec 2017

12 Jan 2018, 08:17

If you want to make your own plots,

Code: Select all

SetBatchLines -1
#NoEnv
#include SVGraph.ahk
Gui, Add, ActiveX, w500 h500 vIE, Shell.Explorer
SVGraph_Attach(IE)
SVGraph_Start()

SVGraph_Chart(400, 400, 20)
SVGraph_SetAxes(0,100, 0, 100,1)
SVGraph_ShowGrid(1)
x:=eqspace(0,100,200)
segs := segmentsFromPoints([0,0], [80,70], [100,110])
segs := segmentsFromPoints([0,0], [20,30], [50,50],[80,70], [100,100])
y:=evalArr(x, func("f").bind(segs))

SVGraph_LinePlot2(x,y,"Red","1.5mm",,"MX")
Gui,show,,Plot
return


segmentsFromPoints(pts*){
	pts := pts.clone()
	p0 := pts.removeat(1)
	segs := []
	for i, p1 in pts
		segs.push( {k : (p1.2-p0.2) / (p1.1-p0.1) , m : ( p1.1*p0.2 - p0.1*p1.2 ) / (p1.1-p0.1), end : p1.1 } ), p0 := p1
	return segs
}

f(segs, x){
	for i, seg in segs
		if (x <= seg.end)
			return x*seg.k+seg.m
}

evalArr(arr,f)
{
	fofx:=[]
	for k, x in arr
		fofx.Push(f.Call(x))
	return fofx
}


eqspace(a,b,n)
{
	t:=(b-a)/(n-1)
	e:=object(), e.SetCapacity(n)
	loop, % n
		e[A_Index]:=(a+(A_Index-1)*t) 
	return e
} 
GuiClose:
	ExitApp
return
In the same folder, save SVGraph.ahk, SVGraph.js, SVGraph.html (all found here: here) and the library D3 v4 (d3.v4.js)
@ bug: (x < seg.end) :arrow: (x <= seg.end) in f(.) :D
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

Re: Universal Control Remapper (UCR) - v0.1.19 26th Dec 2017

12 Jan 2018, 09:05

Wow, this code seems insanely useful!

Example setting: 0,0|10,0|60,80|70,80|100,100
Deadzone from 0-10%
11%-60% maps to 0%-80% of output
Deadzone from 61%-80% (Output remains at 80%)
80%-100% maps normally.

Code: Select all

#SingleInstance force

sc := new SegmentControl(Func("GuiControlCallback"), "w100 h100")
setting := "0,0|10,0|60,80|70,80|100,100"

Gui, Add, Text, xm y+10, Hidden Settings Control
Gui, Add, Edit, w125 xm y+5 hwndhSettingsEdit, % setting

Gui, Add, Text, xm y+5, Input Simulation
Gui, Add, Slider, xm w125 y+5 gInputSliderChanged vSliderValue

sc.LoadSettings(setting)

Gui, Show, x0 y0
return

GuiClose:
	ExitApp

InputSliderChanged:
	Gui, Submit, NoHide
	ToolTip % SliderValue " = " sc.GetValue(SliderValue)
	return

GuiControlCallback(value){
	global hSettingsEdit
	
	GuiControl, , % hSettingsEdit, % value
}

class SegmentControl {
	Segments := []
	Points := []
	
	__New(callback, options := ""){
		this.callback := callback
		Gui, Add, ListView, % "hwndhwnd " options, Input|Output
		this.hLV := hwnd
		Gui, Add, Button, hwndhwnd x+5 yp w20 h100, -
		fn := this.DeleteClicked.Bind(this)
		GuiControl, +g, % hwnd, % fn
		Gui, Add, Edit, xm y+5 hwndhwnd w45
		this.hEditInput := hwnd
		Gui, Add, Edit, x+10 yp hwndhwnd w45
		this.hEditOutput := hwnd
		Gui, Add, Button, hwndhwnd x+5 yp w20, +
		fn := this.AddClicked.Bind(this)
		GuiControl, +g, % hwnd, % fn
	}
	
	; Set save to false when loading settings, to avoid triggering save on load
	_AddPoint(x, y, save := true){
		this.Points.push([x, y])
		this.BuildSegments()
		if (save){
			this.SaveSettings()
		}
	}
	
	_RemovePoint(index){
		this.Points.RemoveAt(row)
		this.BuildSegments()
		this.SaveSettings()
	}
	
	LoadSettings(str){
		chunks := StrSplit(str, "|")
		for i, pt in chunks {
			p := StrSplit(pt, ",")
			this._AddPoint(p[1], p[2], false)
		}
	}
	
	SaveSettings(){
		str := ""
		for i, chunk in this.Points {
			if (i > 1){
				str .= "|"
			}
			str .= chunk[1] "," chunk[2]
		}
		this.callback.Call(str)
	}
	
	BuildSegments(){
		Gui, ListView, % hLV
		LV_Delete()
		for i, p in this.Points {
			LV_Add(, p[1], p[2])
		}
		pts := this.Points.clone()
		p0 := pts.removeat(1)
		this.Segments := []
		for i, p1 in pts {
			this.Segments.push( {k : (p1.2-p0.2) / (p1.1-p0.1) , m : ( p1.1*p0.2 - p0.1*p1.2 ) / (p1.1-p0.1), end : p1.1 } ), p0 := p1
		}
	}
	
	AddClicked(){
		GuiControlGet, low, , % this.hEditInput
		GuiControlGet, high, , % this.hEditOutput
		if (this.IsNumeric(low) && this.IsNumeric(high)){
			this._AddPoint(low, high)
		}
	}
	
	DeleteClicked(){
		Gui, ListView, % hLV
		row := LV_GetNext()
		this._RemovePoint(row)
	}
	
	IsNumeric(str){
		if str is number
			return true
		return false
	}
	
	GetValue(x){
		for i, seg in this.Segments
			if (x <= seg.end)
				return x*seg.k+seg.m
	}
}
Oliver
Posts: 27
Joined: 11 Jan 2018, 10:50

Re: Universal Control Remapper (UCR) - v0.1.19 26th Dec 2017

12 Jan 2018, 09:18

@ Helgef, the idea is to assign a user defined input range of a physical joystick to a user defined input range of a virtual joystick (or throttle, or wheel, or pedals, or....). One example would be that I want throttles idle/off to 70% to translate to throttles idle/off to 80% on the virtual throttles. 70% to max on the physical one should translate to 80% to max on the virtual one.

Aircraft with afterburners are usually designed so that around 80% of throttle travel lays the point where the A/B kicks in. However, most gaming hardware does not offer the option of setting the detent point (the point where the A/B should kick in) to a point of my choice among the throttle lever's travel. My throttle for instance has the detent point at approx. 71% of throttle travel.

@ evilC, you've posted some snippets. If you need to me to test something, let me know.
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

Re: Universal Control Remapper (UCR) - v0.1.19 26th Dec 2017

12 Jan 2018, 09:29

Have a play with that last snippet. In theory, the logic in this last snippet should be able to totally replace the deadzone system AND allow *any number* of detent mappings.

eg lets say your throttle is wobbly from 0% to 5% (So you want a deadzone here), then you want to match the detent @ 60% physical/80% virtual, the settings would be:

Code: Select all

0 0         (0,0 seems to be required at beginning, will probably hide this in UI)
5 0         ( Set dz below 5% input)
60 80      (Sync detent positions)
100 100  (100,100 seems to be required at end, will probably hide this in UI)
Oliver
Posts: 27
Joined: 11 Jan 2018, 10:50

Re: Universal Control Remapper (UCR) - v0.1.19 26th Dec 2017

12 Jan 2018, 13:07

When pasting the above snippet I get an error saying the plugin is missing some lines and will therefore not be included in the list. What am I missing?
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: Universal Control Remapper (UCR) - v0.1.19 26th Dec 2017

12 Jan 2018, 14:01

Thanks for the clarification Oliver. This snippet is a standalone script. Save as x.ahk and double click.

Edit: I made a function for making step-graphs, for no particular reason, anyways,

Code: Select all

segs := stepSegs([20,50], [50,75], [80,100])	; means step to 50 at 20, step to 75 at 50 and step to 100 at 80
stepSegs(l*){
	pts :=  [[0,0]]
	i:=1
	for k, v in l
		pts.push([v.1, pts[i].2]), i:=pts.push(v)
	pts.push([100,pts[pts.length()].2])
	return segmentsFromPoints(pts*)
}
step.jpg
step.jpg (46.09 KiB) Viewed 3498 times
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

Re: Universal Control Remapper (UCR) - v0.1.19 26th Dec 2017

12 Jan 2018, 15:50

Oliver wrote:When pasting the above snippet I get an error saying the plugin is missing some lines and will therefore not be included in the list. What am I missing?
The snippets we are playing with at the moment are stand-alone again.
Just trying to build a UI element that allows us to configure the options for Helgef's multi-breakpoint code
DudeMcRocks
Posts: 1
Joined: 12 Jan 2018, 18:26

Re: Universal Control Remapper (UCR) - v0.1.19 26th Dec 2017

12 Jan 2018, 18:48

Hi, So I just got this program today so im quite new with it. I no problems setting it up untill i tried to use the mosue axis to joystick axis to control the right stick of my xbox controller. Ill move my mouse left and it will move it up or right, or it will detect its moving left but very very delayed. I have 2 monitors so im not sure if thats the problem. I have attached a video link which shows what im talking about. Srry if this is stupid haha. https://youtu.be/HXpkZwLWWgw
Oliver
Posts: 27
Joined: 11 Jan 2018, 10:50

Re: Universal Control Remapper (UCR) - v0.1.19 26th Dec 2017

13 Jan 2018, 06:34

I must say, the standalone with Helgef's multi breakpoint code, I can't really understand how to use it... I'm getting a very small dialog window that I can't resize and it's not really self explanatory. Currently still using evilC's initial script that I run as a plugin. I just have 2 copies of the UCR root, one for each profile as I can't set different profiles yet.
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

Re: Universal Control Remapper (UCR) - v0.1.19 26th Dec 2017

13 Jan 2018, 06:58

The latest script does not do anything useful at all, it is just a mock-up of a proposed UI to add to UCR.
The slider at the bottom imitates the input axis, the "hidden settings control" is what will end up in the settings file, but the rest of the gui is used to configure the breakpoints.
In this configuration, it would sync the detents at 60% physical / 80% virtual

Image

Return to “Gaming Scripts (v1)”

Who is online

Users browsing this forum: No registered users and 57 guests