I built an SoC model for my old employer and a specific battery using a programmable power supply and programmable load, along with a fair bit of python to control them, plus a hot plate and pot of water.
What I did was charge the battery fully at room temperature. Then, I discharged the battery at 0.1C. every few minutes I would stop discharging and then load/charge the battery at several levels, including 0, measuring the voltage at each load/charge rate and pausing several seconds between each measurement, then resume discharging, repeat.
For example for our battery, the battery overall discharge was at 100mA, but when taking measurements, I used -200, -100, -50, 0, 50, 100, 200mA.
After discharging the battery completely, I charged the battery again and discharged it again at a different temperature; 0C, room temp 23C, 40C, 60C, and 80C (we were using a battery that claimed it should handle that ). Temperature was controlled by immersion in Ziploc baggy in heated water or ice water with thermocouple to monitor)
Then this was repeated with several copies of the same battery model.
After I collected all this data, more python scripts converted this data in an SoC as a function of measured voltage, load, and temperature. You can use a fitted polynomial or just a 3D lookup table and interpolation.
Collecting the data took several weeks of careful administration of the setup.
The final outcome was a pretty decent, not perfect model of battery soc that was reliable across a wide temp range and loading, which was important to use since we could not in our application just totally unload the battery to get a Voc measurement.
It was a lot of effort, though.