Pages: [1]
Author Topic: trim heatmap tool for tuning KFKHFM  (Read 25070 times)
nyet
Administrator
Hero Member
*****

Karma: +610/-169
Offline Offline

Posts: 12324


WWW
« on: October 24, 2024, 01:20:02 AM »

Similar to the FKVVS tuner, this is specifically made to tune KFKHFM based on fr/frm logs.

https://github.com/nyetwurk/trim-heatmap/releases

It is command line based, so you have to give it one or more log files on the command line. If you do not know what that means, this tool is not for you



Code:
usage: heatmap.py [-h] [-w WINDOW] [-l LOAD_FILTER] [-r RPM_FILTER] [-m MIN_SAMPLES] [-f] [-u] [-v] [filename ...]

Create trim heatmap for KFKHFM based on fr/frm datalog

positional arguments:
  filename              csv files(s) to parse (log.csv)

options:
  -h, --help            show this help message and exit
  -w WINDOW, --window WINDOW
                        number of sequential rows to detect constant rpm/load (5)
  -l LOAD_FILTER, --load-filter LOAD_FILTER
                        change in load which is still "constant" load (10)
  -r RPM_FILTER, --rpm-filter RPM_FILTER
                        change in RPM which is still "constant" RPM (100)
  -m MIN_SAMPLES, --min-samples MIN_SAMPLES
                        minimum number of samples required to generate a cell (10)
  -f, --use-fr          use "fr" instead of "frm" (the default)
  -u, --use-unweighted-mean
                        use unweighted mean instead of weighted average (the default)
  -v, --verbose
« Last Edit: October 24, 2024, 01:22:52 AM by nyet » Logged

ME7.1 tuning guide
ECUx Plot
ME7Sum checksum
Trim heatmap tool

Please do not ask me for tunes. I'm here to help people make their own.

Do not PM me technical questions! Please, ask all questions on the forums! Doing so will ensure the next person with the same issue gets the opportunity to learn from your ex
ratosluaf
Jr. Member
**

Karma: +1/-3
Offline Offline

Posts: 39


« Reply #1 on: January 06, 2025, 03:31:43 AM »

I've done something similar in the past for myself.
Now I had some free time so I've rewritten it to streamlit and deployed it on the community cloud.

Here's the link: https://fixhfm.streamlit.app/

It does depend on nmot_w, rl_w and fr_w from me7logger, so idk if it will work with all logs. It does stick your data to nearest nmot_w and rl_w in kfkhfm table and calculate it depending on weighted mean.

You can copy your table from tunerpro or import your table from csv file - example of log file and map csv file in github repo.

github repo: https://github.com/rwalkuski/hfm_fixer

Any feedback is welcome since now i have no possibility to test output myself, but had success with my own car in the past.


Edit: for now, when you want to copy from site to tunerpro, please buffer the data in notepad or any other text editor Copying directly into tunerpro causes destroying of data table, but i don't know why for now.
edit2: copying directly from site lacks of carriage return which tunerpro interprets as new line. At the moment, i do not know how to fix that, so please buffer your data.
« Last Edit: January 06, 2025, 05:09:25 AM by ratosluaf » Logged
Crazy18T
Newbie
*

Karma: +0/-0
Offline Offline

Posts: 17


« Reply #2 on: April 29, 2025, 11:20:31 PM »

Finally have my car back together (in a slightly temporary fashion), so I was eager to try this. Smiley After struggling to get the output to not cause issues with the tune (tried every setting and many iterations), I decided to take a closer look at the code. It seems the bucket creation code is quite broken.

Applying this patch, we can see both RPM and load bucket calculation are way off:
Code:
diff -ur trim-heatmap-0.0.5/src/heatmap.py trim-heatmap-0.0.5-testing/src/heatmap.py
--- trim-heatmap-0.0.5/src/heatmap.py 2024-11-06 02:39:02.000000000 -0700
+++ trim-heatmap-0.0.5-testing/src/heatmap.py 2025-04-29 23:49:57.097326680 -0600
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 
 # internal modules
 import os
@@ -78,6 +78,7 @@
  frdata[lk]={}
  for rk,rv in rpms.items():
  query = f"{rv[0]} <= @lc.rpm <= {rv[1]} & {lv[0]} <= @lc.load <= {lv[1]}"
+ print(query)
  res = lc.query(query).copy()
  if len(res.index) >= (args.min_samples, 1)[args.no_filter]: # ternary is (false, true)
  # size of cell, center to corner

A few rounds of output with patch applied (example log.csv):
Quote
$ ./heatmap.py
0 <= @lc.rpm <= 859.0 & 0 <= @lc.load <= 14.0
860.0 <= @lc.rpm <= 1119.0 & 0 <= @lc.load <= 14.0
1120.0 <= @lc.rpm <= 1379.0 & 0 <= @lc.load <= 14.0
1380.0 <= @lc.rpm <= 1759.0 & 0 <= @lc.load <= 14.0
1760.0 <= @lc.rpm <= 2259.0 & 0 <= @lc.load <= 14.0
2260.0 <= @lc.rpm <= 2759.0 & 0 <= @lc.load <= 14.0
2760.0 <= @lc.rpm <= 3259.0 & 0 <= @lc.load <= 14.0
3260.0 <= @lc.rpm <= 3759.0 & 0 <= @lc.load <= 14.0
3760.0 <= @lc.rpm <= 4259.0 & 0 <= @lc.load <= 14.0
4260.0 <= @lc.rpm <= 4759.0 & 0 <= @lc.load <= 14.0
4760.0 <= @lc.rpm <= 5259.0 & 0 <= @lc.load <= 14.0
5260.0 <= @lc.rpm <= 5759.0 & 0 <= @lc.load <= 14.0
5760.0 <= @lc.rpm <= 6259.0 & 0 <= @lc.load <= 14.0
6260.0 <= @lc.rpm <= 10000 & 0 <= @lc.load <= 14.0
0 <= @lc.rpm <= 859.0 & 15.0 <= @lc.load <= 24.125
860.0 <= @lc.rpm <= 1119.0 & 15.0 <= @lc.load <= 24.125
1120.0 <= @lc.rpm <= 1379.0 & 15.0 <= @lc.load <= 24.125
1380.0 <= @lc.rpm <= 1759.0 & 15.0 <= @lc.load <= 24.125
1760.0 <= @lc.rpm <= 2259.0 & 15.0 <= @lc.load <= 24.125
2260.0 <= @lc.rpm <= 2759.0 & 15.0 <= @lc.load <= 24.125
2760.0 <= @lc.rpm <= 3259.0 & 15.0 <= @lc.load <= 24.125
3260.0 <= @lc.rpm <= 3759.0 & 15.0 <= @lc.load <= 24.125
3760.0 <= @lc.rpm <= 4259.0 & 15.0 <= @lc.load <= 24.125
4260.0 <= @lc.rpm <= 4759.0 & 15.0 <= @lc.load <= 24.125
4760.0 <= @lc.rpm <= 5259.0 & 15.0 <= @lc.load <= 24.125
5260.0 <= @lc.rpm <= 5759.0 & 15.0 <= @lc.load <= 24.125
5760.0 <= @lc.rpm <= 6259.0 & 15.0 <= @lc.load <= 24.125
6260.0 <= @lc.rpm <= 10000 & 15.0 <= @lc.load <= 24.125
0 <= @lc.rpm <= 859.0 & 25.125 <= @lc.load <= 33.875
860.0 <= @lc.rpm <= 1119.0 & 25.125 <= @lc.load <= 33.875
1120.0 <= @lc.rpm <= 1379.0 & 25.125 <= @lc.load <= 33.875
1380.0 <= @lc.rpm <= 1759.0 & 25.125 <= @lc.load <= 33.875
1760.0 <= @lc.rpm <= 2259.0 & 25.125 <= @lc.load <= 33.875
2260.0 <= @lc.rpm <= 2759.0 & 25.125 <= @lc.load <= 33.875
2760.0 <= @lc.rpm <= 3259.0 & 25.125 <= @lc.load <= 33.875
3260.0 <= @lc.rpm <= 3759.0 & 25.125 <= @lc.load <= 33.875
3760.0 <= @lc.rpm <= 4259.0 & 25.125 <= @lc.load <= 33.875
4260.0 <= @lc.rpm <= 4759.0 & 25.125 <= @lc.load <= 33.875
4760.0 <= @lc.rpm <= 5259.0 & 25.125 <= @lc.load <= 33.875
5260.0 <= @lc.rpm <= 5759.0 & 25.125 <= @lc.load <= 33.875
5760.0 <= @lc.rpm <= 6259.0 & 25.125 <= @lc.load <= 33.875
6260.0 <= @lc.rpm <= 10000 & 25.125 <= @lc.load <= 33.875
0 <= @lc.rpm <= 859.0 & 34.875 <= @lc.load <= 44.0
860.0 <= @lc.rpm <= 1119.0 & 34.875 <= @lc.load <= 44.0
1120.0 <= @lc.rpm <= 1379.0 & 34.875 <= @lc.load <= 44.0
1380.0 <= @lc.rpm <= 1759.0 & 34.875 <= @lc.load <= 44.0
1760.0 <= @lc.rpm <= 2259.0 & 34.875 <= @lc.load <= 44.0
2260.0 <= @lc.rpm <= 2759.0 & 34.875 <= @lc.load <= 44.0
2760.0 <= @lc.rpm <= 3259.0 & 34.875 <= @lc.load <= 44.0
3260.0 <= @lc.rpm <= 3759.0 & 34.875 <= @lc.load <= 44.0
3760.0 <= @lc.rpm <= 4259.0 & 34.875 <= @lc.load <= 44.0
4260.0 <= @lc.rpm <= 4759.0 & 34.875 <= @lc.load <= 44.0
4760.0 <= @lc.rpm <= 5259.0 & 34.875 <= @lc.load <= 44.0
5260.0 <= @lc.rpm <= 5759.0 & 34.875 <= @lc.load <= 44.0
5760.0 <= @lc.rpm <= 6259.0 & 34.875 <= @lc.load <= 44.0
6260.0 <= @lc.rpm <= 10000 & 34.875 <= @lc.load <= 44.0
Logged
nyet
Administrator
Hero Member
*****

Karma: +610/-169
Offline Offline

Posts: 12324


WWW
« Reply #3 on: April 29, 2025, 11:57:17 PM »

post the log ill take a look

what are you expecting to see?
« Last Edit: April 29, 2025, 11:59:11 PM by nyet » Logged

ME7.1 tuning guide
ECUx Plot
ME7Sum checksum
Trim heatmap tool

Please do not ask me for tunes. I'm here to help people make their own.

Do not PM me technical questions! Please, ask all questions on the forums! Doing so will ensure the next person with the same issue gets the opportunity to learn from your ex
Crazy18T
Newbie
*

Karma: +0/-0
Offline Offline

Posts: 17


« Reply #4 on: April 30, 2025, 12:03:42 AM »

This is the example log.csv as stated. I assume the bucket code is AI generated? Why do you not see the issue here?
Logged
Crazy18T
Newbie
*

Karma: +0/-0
Offline Offline

Posts: 17


« Reply #5 on: April 30, 2025, 12:34:06 AM »

I expect to see the RPM and load buckets populated as you provided (rpms loads) for the 2.7T. In my case they are adjusted for my setup, but that's beyond the scope of this thread. Sorry! I didn't mean to come across as rude.
Logged
nyet
Administrator
Hero Member
*****

Karma: +610/-169
Offline Offline

Posts: 12324


WWW
« Reply #6 on: April 30, 2025, 10:39:27 AM »

I expect to see the RPM and load buckets populated as you provided (rpms loads) for the 2.7T. In my case they are adjusted for my setup, but that's beyond the scope of this thread. Sorry! I didn't mean to come across as rude.

You're not printing the output. You're printing a query that happens in the core code, before anything else happens, including all processing. What is the input you are giving it? What command line parameters?
« Last Edit: April 30, 2025, 10:41:11 AM by nyet » Logged

ME7.1 tuning guide
ECUx Plot
ME7Sum checksum
Trim heatmap tool

Please do not ask me for tunes. I'm here to help people make their own.

Do not PM me technical questions! Please, ask all questions on the forums! Doing so will ensure the next person with the same issue gets the opportunity to learn from your ex
Crazy18T
Newbie
*

Karma: +0/-0
Offline Offline

Posts: 17


« Reply #7 on: April 30, 2025, 03:31:55 PM »

I'm aware I'm not printing the output, but the output can't be correct if the buckets/cells aren't populated using the proper values. The command line is included with my post. It's just showing what buckets are created using your example log.csv.
Logged
Crazy18T
Newbie
*

Karma: +0/-0
Offline Offline

Posts: 17


« Reply #8 on: April 30, 2025, 03:58:17 PM »

calc_ranges() seems to be completely broken. I don't understand how you aren't seeing this? It creates rpms as 14 buckets/cells as expected, but for example the first bucket shouldn't be 0-859. It should be 720-999. And it continues on with both bucket ranges in this confusing way.
Logged
nyet
Administrator
Hero Member
*****

Karma: +610/-169
Offline Offline

Posts: 12324


WWW
« Reply #9 on: April 30, 2025, 10:08:57 PM »

the map numbers are the *centers* of the cells. We either do a linear average or a weighted average by distance from the center of the cell (which is the map axis data)
« Last Edit: April 30, 2025, 10:11:22 PM by nyet » Logged

ME7.1 tuning guide
ECUx Plot
ME7Sum checksum
Trim heatmap tool

Please do not ask me for tunes. I'm here to help people make their own.

Do not PM me technical questions! Please, ask all questions on the forums! Doing so will ensure the next person with the same issue gets the opportunity to learn from your ex
Crazy18T
Newbie
*

Karma: +0/-0
Offline Offline

Posts: 17


« Reply #10 on: June 26, 2025, 10:04:27 PM »

Appreciate the clarification. As you probably figured out, my issues actually stemmed from minor hardware issues. I would like to continue on in helping to develop open source tools to help dial in air/fuel on ME7. I was able to create a KFLF version for ME7.5 1.8T no problem. The reason being, we have additional ranges on this load/air correction map that could help smooth things out.

The obvious next step is to create the inverse map using the same logic, so we can try and determine when we have an air/load correction situation vs just a non-linearity issue with injector pulse. My first attempt to do this works in continuous mode, but it provides no output in other modes. Do you think this is because calc_ranges() doesn't tolerate the small floating points used in IOT of FKKVS? It's the best guess I have at this point. Appreciate the code!

Quote
$ trim-heatmap-fkkvs/src/heatmap.py log.csv --text -s1
Empty DataFrame
Columns: []
Index: []
$ trim-heatmap-fkkvs/src/heatmap.py log.csv -s1
Traceback (most recent call last):
  File "/home/tuning/trim-heatmap-fkkvs/src/heatmap.py", line 255, in <module>
    main()
  File "/home/tuning/trim-heatmap-fkkvs/src/heatmap.py", line 246, in main
    sbs.heatmap(heatmap, annot=(not args.continuous), center=0, cmap='PiYG', cbar_kws={'label': '% trim'}).invert_yaxis()
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/seaborn/matrix.py", line 446, in heatmap
    plotter = _HeatMapper(data, vmin, vmax, cmap, center, robust, annot, fmt,
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/seaborn/matrix.py", line 163, in __init__
    self._determine_cmap_params(plot_data, vmin, vmax,
  File "/usr/lib/python3/dist-packages/seaborn/matrix.py", line 202, in _determine_cmap_params
    vmin = np.nanmin(calc_data)
           ^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/numpy/lib/nanfunctions.py", line 343, in nanmin
    res = np.fmin.reduce(a, axis=axis, out=out, **kwargs)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ValueError: zero-size array to reduction operation fmin which has no identity
« Last Edit: June 26, 2025, 10:34:58 PM by Crazy18T » Logged
Crazy18T
Newbie
*

Karma: +0/-0
Offline Offline

Posts: 17


« Reply #11 on: June 27, 2025, 11:54:33 PM »

Changes for the FKKVS variation are quite simple, but I'm still not sure exactly the logic of calc_ranges(). I have no reason to think it's anything else given it works in continuous mode. Maybe I'm missing something?

Code:
diff -ur trim-heatmap-0.0.5/src/heatmap.py trim-heatmap-fkkvs-mod/src/heatmap.py
--- trim-heatmap-0.0.5/src/heatmap.py 2024-11-06 02:39:02.000000000 -0700
+++ trim-heatmap-fkkvs-mod/src/heatmap.py 2025-06-28 00:39:48.101375975 -0600
@@ -58,9 +58,9 @@
  return buckets
 
 def continuous_heatmap(lc):
- lc['load'] = lc['load'].round(0)
+ lc['iot'] = lc['iot'].round(0)
  lc['rpm'] = (lc['rpm']/10).round(0) * 10
- heatmap = lc.pivot_table(index='load', columns='rpm', values='fr')
+ heatmap = lc.pivot_table(index='iot', columns='rpm', values='fr')
  return heatmap.dropna(how='all', axis=0).dropna(how='all', axis=1)
 
 def distance(x0, y0, x1, y1):
@@ -68,23 +68,22 @@
 
 def quantized_heatmap(lc, args):
  # create bucket ranges
- rpms = calc_ranges([720, 1000, 1240, 1520, 2000, 2520, 3000, 3520, 4000, 4520, 5000, 5520, 6000, 6520], 10000)
- loads = calc_ranges([9.75, 20.25, 30, 39.75, 50.25, 60, 69.75, 80.25, 90, 99.75, 110.25, 120, 140.25, 159.75], 300)
- #loads = calc_ranges([9.75, 20.25, 30, 39.75, 50.25, 60, 69.75, 80.25, 90, 99.75, 110.25, 140.25, 150, 168])
+ rpms = calc_ranges([600, 800, 1000, 1240, 1520, 1720, 2000, 2520, 3000, 3520, 4000, 4520, 5000, 5520, 6000, 6520], 10000)
+ iots = calc_ranges([0.997, 1.299, 1.6, 2, 2.499, 3.003, 4, 5.003, 6.001, 7.004, 8.001, 9.004, 10.001, 13.004, 16.002, 19.005], 30)
 
  # sort lambda control (fr/frm) into buckets
  frdata={}
- for lk,lv in loads.items():
+ for lk,lv in iots.items():
  frdata[lk]={}
  for rk,rv in rpms.items():
- query = f"{rv[0]} <= @lc.rpm <= {rv[1]} & {lv[0]} <= @lc.load <= {lv[1]}"
+ query = f"{rv[0]} <= @lc.rpm <= {rv[1]} & {lv[0]} <= @lc.iot <= {lv[1]}"
  res = lc.query(query).copy()
  if len(res.index) >= (args.min_samples, 1)[args.no_filter]: # ternary is (false, true)
  # size of cell, center to corner
  cellradius = distance(lv[0], rv[0], lv[1], rv[1])/2
  # find distance to cell center
  # FIXME: this is not right for cells at edges of map, but we have to weight data off the map somehow
- res['distance'] = res.apply(lambda row: distance(row.load, row.rpm, lk, rk), axis=1)
+ res['distance'] = res.apply(lambda row: distance(row.iot, row.rpm, lk, rk), axis=1)
  # Weight is proportional to closeness to center: distance = 0 has highest weight
  # Don't let weights get negative due to weird radius calcs
  res['weight'] = np.maximum((cellradius-res.distance)/cellradius, 0.1)
@@ -109,9 +108,9 @@
 def main():
  parser = argparse.ArgumentParser(description='Create trim heatmap for KFKHFM based on fr/frm datalog')
  parser.add_argument('filename', default=['log.csv'], nargs='*', help='csv files(s) to parse (log.csv)')
- parser.add_argument('-w', '--window', type=int, default=10, help='number of sequential rows to detect constant rpm/load (10)')
+ parser.add_argument('-w', '--window', type=int, default=10, help='number of sequential rows to detect constant rpm/iot (10)')
 
- parser.add_argument('-l', '--load-filter', type=float, default=10, help='change in load which is still "constant" load (10)')
+ parser.add_argument('-i', '--iot-filter', type=float, default=0.001, help='change in iot which is still "constant" iot (0.001)')
  parser.add_argument('-r', '--rpm-filter', type=int, default=100, help='change in RPM which is still "constant" RPM (100)')
  parser.add_argument('-m', '--maf-filter', type=float, default=10, help='change in MAF which is still "constant" MAF (10)')
 
@@ -171,13 +170,13 @@
  df.rename(str.strip, axis='columns', inplace=True)
 
  # pick rl or frm_w
- whichrl = ('rl', 'rl_w')['rl_w' in df]
+ whichrl = ('FIXME', 'ti_b1')['ti_b1' in df]
  # pick nmot or nmot_w
  whichnmot = ('nmot', 'nmot_w')['nmot_w' in df]
  # pick frm or fr
  whichfr = ('frm', 'fr')[args.use_fr or 'frm_w' not in df]
 
- rows = [whichnmot, whichrl, whichfr + '_w', 'mshfm_w']
+ rows = [whichnmot, whichrl, whichfr + '_w']
 
  if whichfr + '2_w' in df:
  rows.append(whichfr + '2_w')
@@ -185,11 +184,10 @@
  if args.verbose:
  print(f"using {rows} from log")
 
- # grab only the things we need, rename rl/nmot/mshfm to load/rpm/maf
+ # grab only the things we need, rename rl/nmot to iot/rpm
  lc = df[rows]. \
- rename(columns={whichrl:'load'}). \
- rename(columns={whichnmot:'rpm'}). \
- rename(columns={'mshfm_w':'maf'})
+ rename(columns={whichrl:'iot'}). \
+ rename(columns={whichnmot:'rpm'})
 
  # flatten index
  lc.columns = lc.columns.get_level_values(0)
@@ -204,15 +202,12 @@
 
  # set up filter source data
  lc['rpm_delta'] = lc.rpm.rolling(window=args.window).apply(lambda x: x.max() - x.min())
- lc['load_delta'] = lc.load.rolling(window=args.window).apply(lambda x: x.max() - x.min())
- lc['maf_delta'] = lc.maf.rolling(window=args.window).apply(lambda x: x.max() - x.min())
+ lc['iot_delta'] = lc.iot.rolling(window=args.window).apply(lambda x: x.max() - x.min())
 
  if not args.no_filter:
  # tag rows we want to use based on source data
  lc['use'] = \
- lc.rpm_delta.notnull()  & (lc.rpm_delta  <= args.rpm_filter)  & \
- lc.load_delta.notnull() & (lc.load_delta <= args.load_filter) & \
- lc.maf_delta.notnull()  & (lc.maf_delta  <= args.maf_filter)
+ lc.rpm_delta.notnull()  & (lc.rpm_delta  <= args.rpm_filter)
 
  #print(lc[(abs(lc.fr)>5) & (lc.rpm_delta > 0)].to_string())
 
@@ -245,7 +240,7 @@
  sbs.heatmap(heatmap, annot=(not args.continuous), center=0, cmap='PiYG', cbar_kws={'label': '% trim'}).invert_yaxis()
 
  plt.xlabel('RPM')
- plt.ylabel('Load')
+ plt.ylabel('IOT')
 
  signal.signal(signal.SIGINT, signal_handler)
  plt.show()
Logged
Crazy18T
Newbie
*

Karma: +0/-0
Offline Offline

Posts: 17


« Reply #12 on: June 29, 2025, 08:13:39 PM »

After confirming my suspicions and studying calc_ranges(), I now understand how it works and have created a working patch for FKKVS. It could still use a bit of work, but I don't have a lot of time to work on this stuff right now, so it's a bit quick and dirty. Hope someone else finds this useful! Smiley

Code:
diff -ur trim-heatmap-0.0.5/src/heatmap.py trim-heatmap-fkkvs-mod/src/heatmap.py
--- trim-heatmap-0.0.5/src/heatmap.py 2024-11-06 02:39:02.000000000 -0700
+++ trim-heatmap-fkkvs-mod/src/heatmap.py 2025-06-29 21:06:28.834621188 -0600
@@ -44,23 +44,34 @@
  except subprocess.CalledProcessError:
  return True
 
+def calc_median(num1, num2):
+  median_value = (num1 + num2) / 2
+  return median_value
+
 def calc_ranges(ary, max):
- buckets = {}
- for i,v in enumerate(ary):
- if i == 0:
- # very start
- buckets[v] = [0, (ary[i]+ary[i+1])/2-1]
- elif i != len(ary)-1:
- buckets[v] = [(ary[i-1]+ary[i])/2, (ary[i]+ary[i+1])/2-1]
- else:
- # very end
- buckets[v] = [(ary[i-1]+ary[i])/2, max]
- return buckets
+    buckets = {}
+    for i,v in enumerate(ary):
+        if i == 0:
+            # very start
+            #buckets[v] = [0, (ary[i]+ary[i+1])/2-1]
+            if i > 30:
+                buckets[v] = [0, calc_median(ary[i],ary[i+1])-1]
+            else:
+                buckets[v] = [0, calc_median(ary[i],ary[i+1])-0.001]
+        elif i != len(ary)-1:
+            if i > 30:
+                buckets[v] = [calc_median(ary[i-1],ary[i]), calc_median(ary[i],ary[i+1])-1]
+            else:
+                buckets[v] = [calc_median(ary[i-1],ary[i]), calc_median(ary[i],ary[i+1])-0.001]
+        else:
+            # very end
+            buckets[v] = [(ary[i-1]+ary[i])/2, max]
+    return buckets
 
 def continuous_heatmap(lc):
- lc['load'] = lc['load'].round(0)
+ lc['iot'] = lc['iot'].round(0)
  lc['rpm'] = (lc['rpm']/10).round(0) * 10
- heatmap = lc.pivot_table(index='load', columns='rpm', values='fr')
+ heatmap = lc.pivot_table(index='iot', columns='rpm', values='fr')
  return heatmap.dropna(how='all', axis=0).dropna(how='all', axis=1)
 
 def distance(x0, y0, x1, y1):
@@ -68,23 +79,23 @@
 
 def quantized_heatmap(lc, args):
  # create bucket ranges
- rpms = calc_ranges([720, 1000, 1240, 1520, 2000, 2520, 3000, 3520, 4000, 4520, 5000, 5520, 6000, 6520], 10000)
- loads = calc_ranges([9.75, 20.25, 30, 39.75, 50.25, 60, 69.75, 80.25, 90, 99.75, 110.25, 120, 140.25, 159.75], 300)
- #loads = calc_ranges([9.75, 20.25, 30, 39.75, 50.25, 60, 69.75, 80.25, 90, 99.75, 110.25, 140.25, 150, 168])
+    # 1.8T
+ rpms = calc_ranges([600, 800, 1000, 1240, 1520, 1720, 2000, 2520, 3000, 3520, 4000, 4520, 5000, 5520, 6000, 6520], 10000)
+ iots = calc_ranges([0.997, 1.299, 1.6, 2, 2.499, 3.003, 4, 5.003, 6.001, 7.004, 8.001, 9.004, 10.001, 13.004, 16.002, 19.005], 30)
 
  # sort lambda control (fr/frm) into buckets
  frdata={}
- for lk,lv in loads.items():
+ for lk,lv in iots.items():
  frdata[lk]={}
  for rk,rv in rpms.items():
- query = f"{rv[0]} <= @lc.rpm <= {rv[1]} & {lv[0]} <= @lc.load <= {lv[1]}"
+ query = f"{rv[0]} <= @lc.rpm <= {rv[1]} & {lv[0]} <= @lc.iot <= {lv[1]}"
  res = lc.query(query).copy()
  if len(res.index) >= (args.min_samples, 1)[args.no_filter]: # ternary is (false, true)
  # size of cell, center to corner
  cellradius = distance(lv[0], rv[0], lv[1], rv[1])/2
  # find distance to cell center
  # FIXME: this is not right for cells at edges of map, but we have to weight data off the map somehow
- res['distance'] = res.apply(lambda row: distance(row.load, row.rpm, lk, rk), axis=1)
+ res['distance'] = res.apply(lambda row: distance(row.iot, row.rpm, lk, rk), axis=1)
  # Weight is proportional to closeness to center: distance = 0 has highest weight
  # Don't let weights get negative due to weird radius calcs
  res['weight'] = np.maximum((cellradius-res.distance)/cellradius, 0.1)
@@ -109,11 +120,10 @@
 def main():
  parser = argparse.ArgumentParser(description='Create trim heatmap for KFKHFM based on fr/frm datalog')
  parser.add_argument('filename', default=['log.csv'], nargs='*', help='csv files(s) to parse (log.csv)')
- parser.add_argument('-w', '--window', type=int, default=10, help='number of sequential rows to detect constant rpm/load (10)')
+ parser.add_argument('-w', '--window', type=int, default=10, help='number of sequential rows to detect constant rpm/iot (10)')
 
- parser.add_argument('-l', '--load-filter', type=float, default=10, help='change in load which is still "constant" load (10)')
+ parser.add_argument('-i', '--iot-filter', type=float, default=0.001, help='change in iot which is still "constant" iot (0.001)')
  parser.add_argument('-r', '--rpm-filter', type=int, default=100, help='change in RPM which is still "constant" RPM (100)')
- parser.add_argument('-m', '--maf-filter', type=float, default=10, help='change in MAF which is still "constant" MAF (10)')
 
  parser.add_argument('-n', '--no-filter', action='store_true', help='disable filter (default is enabled)')
 
@@ -171,13 +181,13 @@
  df.rename(str.strip, axis='columns', inplace=True)
 
  # pick rl or frm_w
- whichrl = ('rl', 'rl_w')['rl_w' in df]
+ whichrl = ('FIXME', 'ti_b1')['ti_b1' in df]
  # pick nmot or nmot_w
  whichnmot = ('nmot', 'nmot_w')['nmot_w' in df]
  # pick frm or fr
  whichfr = ('frm', 'fr')[args.use_fr or 'frm_w' not in df]
 
- rows = [whichnmot, whichrl, whichfr + '_w', 'mshfm_w']
+ rows = [whichnmot, whichrl, whichfr + '_w']
 
  if whichfr + '2_w' in df:
  rows.append(whichfr + '2_w')
@@ -185,11 +195,10 @@
  if args.verbose:
  print(f"using {rows} from log")
 
- # grab only the things we need, rename rl/nmot/mshfm to load/rpm/maf
+ # grab only the things we need, rename rl/nmot to iot/rpm
  lc = df[rows]. \
- rename(columns={whichrl:'load'}). \
- rename(columns={whichnmot:'rpm'}). \
- rename(columns={'mshfm_w':'maf'})
+ rename(columns={whichrl:'iot'}). \
+ rename(columns={whichnmot:'rpm'})
 
  # flatten index
  lc.columns = lc.columns.get_level_values(0)
@@ -204,15 +213,12 @@
 
  # set up filter source data
  lc['rpm_delta'] = lc.rpm.rolling(window=args.window).apply(lambda x: x.max() - x.min())
- lc['load_delta'] = lc.load.rolling(window=args.window).apply(lambda x: x.max() - x.min())
- lc['maf_delta'] = lc.maf.rolling(window=args.window).apply(lambda x: x.max() - x.min())
+ lc['iot_delta'] = lc.iot.rolling(window=args.window).apply(lambda x: x.max() - x.min())
 
  if not args.no_filter:
  # tag rows we want to use based on source data
  lc['use'] = \
- lc.rpm_delta.notnull()  & (lc.rpm_delta  <= args.rpm_filter)  & \
- lc.load_delta.notnull() & (lc.load_delta <= args.load_filter) & \
- lc.maf_delta.notnull()  & (lc.maf_delta  <= args.maf_filter)
+ lc.rpm_delta.notnull()  & (lc.rpm_delta  <= args.rpm_filter)
 
  #print(lc[(abs(lc.fr)>5) & (lc.rpm_delta > 0)].to_string())
 
@@ -245,7 +251,7 @@
  sbs.heatmap(heatmap, annot=(not args.continuous), center=0, cmap='PiYG', cbar_kws={'label': '% trim'}).invert_yaxis()
 
  plt.xlabel('RPM')
- plt.ylabel('Load')
+ plt.ylabel('IOT')
 
  signal.signal(signal.SIGINT, signal_handler)
  plt.show()
Logged
Crazy18T
Newbie
*

Karma: +0/-0
Offline Offline

Posts: 17


« Reply #13 on: July 01, 2025, 07:35:34 PM »

Not sure why nyet seems to be butthurt that I haven't posted logs. I never asked for help with my tune (only logic questions), and I'm writing open source code. I won't be posting details of my build or any raw logs, but I will demonstrate with today's test that my logic is sound. It is possible to discern where adjustments should be made when certain reference points are lost.

This is a bit of a Top Chef level proof, as I'm providing fueling 3 ways! Wink In this case, a sample size of 6 seemed to be ideal for this smallish 14 MB log at 30s/sec. Again, I hope someone else finds this useful. If you'd like I can provide a patch for 1.8T KFLF as well, but it's pretty straightforward to implement that. If you want it to line up better c/p into a window with a fixed width font. The forum code seems to lose the concept of this with a quote for some odd reason.

Quote
$ hm /mnt/win7/ME7L/logs/pro_20250701_181512.csv --text -s6
           600    800    1000    1240    1520    1720                 2000   2520   3000   3520    4000    4520    5000
9.004      NaN    NaN     NaN     NaN     NaN     NaN                  NaN    NaN  3.227    NaN     NaN     NaN     NaN
8.001      NaN    NaN     NaN     NaN     NaN     NaN                  NaN    NaN  2.420    NaN     NaN     NaN     NaN
7.004      NaN    NaN     NaN     NaN     NaN     NaN                  NaN  4.178  2.463    NaN     NaN     NaN     NaN
6.001      NaN    NaN     NaN     NaN     NaN     NaN                  NaN  5.612  5.282  4.641     NaN     NaN     NaN
5.003      NaN    NaN     NaN     NaN  12.723  12.397                9.112  8.143  7.082  5.859   9.056  10.788     NaN
4.000    0.322  3.446   2.194   7.356  10.733   7.245                7.886  9.283  8.467  9.043   9.769  10.905  12.344
3.003    3.323 -0.489   7.611   5.273   3.701   0.021                2.266  5.367  4.682  7.583   8.830     NaN     NaN
2.499   -3.556 -0.727   5.134   5.487  -0.666   2.313                1.712  3.180  2.952  6.275   1.927     NaN     NaN
2.000  -20.017 -1.064   1.841   8.106   4.447   0.352                3.206  3.007  4.685  4.710   6.505     NaN     NaN
1.600      NaN -3.323   7.188  10.081   6.467   0.030                0.429  1.962  5.093  1.300   5.939     NaN     NaN
1.299      NaN    NaN     NaN   0.000   0.000   0.000                  NaN  0.001  0.007  0.000     NaN     NaN     NaN
           600    800    1000    1240    1520                         2000   2520   3000   3520    4000    4520    5000
100.50     NaN    NaN     NaN     NaN     NaN                          NaN    NaN  2.036    NaN     NaN     NaN     NaN
80.25      NaN    NaN     NaN     NaN     NaN                          NaN  5.515    NaN  5.076     NaN     NaN     NaN
70.50      NaN    NaN     NaN     NaN     NaN                          NaN  7.461  4.075  2.964     NaN     NaN     NaN
60.00      NaN    NaN     NaN   6.291  11.940                        8.644  7.638  8.831  9.017  10.119  10.705     NaN
50.25      NaN  4.823   2.511   4.705  10.077                        7.814 10.083  9.166  9.293   9.571  10.905  12.344
40.50      NaN -0.224   2.092   3.996  -0.365                        0.718  5.134  6.141  8.550   9.886     NaN     NaN
30.00    3.643 -1.541   4.673   5.338  -1.181                        2.250  3.228  2.799  6.395     NaN     NaN     NaN
20.25  -13.443 -2.136   2.233   7.484   5.303                        3.061  2.855  4.409  7.379   6.243     NaN     NaN
15.00      NaN  1.045  13.099  10.523   7.182                        0.592  2.223  5.868  1.695   7.593     NaN     NaN
10.50      NaN    NaN     NaN   0.000   0.000                          NaN  0.000  0.005  0.006     NaN     NaN     NaN
                         1000       1480         1720   1840   1920   2000   2520   3000   3520            4520    5520
102.75                    NaN        NaN          NaN    NaN    NaN    NaN    NaN  2.040    NaN             NaN     NaN
78.00                     NaN        NaN          NaN    NaN    NaN    NaN  5.007  4.441  6.014             NaN     NaN
63.75                     NaN     11.580       12.536    NaN    NaN  8.706  8.482  8.708  7.759          10.499     NaN
49.50                   4.102     11.496        7.491  6.704  8.029  7.488  9.174  9.138  9.604          10.847  13.145
37.50                   2.531     -1.527        1.807  1.932  2.330  1.961  3.986  2.136  7.876             NaN     NaN
15.00                  -1.347      6.553        0.161 -0.007  0.000  0.798  2.479  4.896  2.555             NaN     NaN
9.75                      NaN      0.000          NaN    NaN    NaN    NaN  0.000  0.000  0.000             NaN     NaN
Logged
nyet
Administrator
Hero Member
*****

Karma: +610/-169
Offline Offline

Posts: 12324


WWW
« Reply #14 on: July 01, 2025, 09:50:22 PM »

code uses fixed width, quote does not.
Logged

ME7.1 tuning guide
ECUx Plot
ME7Sum checksum
Trim heatmap tool

Please do not ask me for tunes. I'm here to help people make their own.

Do not PM me technical questions! Please, ask all questions on the forums! Doing so will ensure the next person with the same issue gets the opportunity to learn from your ex
Pages: [1]
  Print  
 
Jump to:  

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines Page created in 0.127 seconds with 17 queries. (Pretty URLs adds 0.016s, 0q)