How to read Spectral-Scan file

I have performed remotely a Spectral-Scan on a Groove-5Hn of a client using the following command

interface wireless spectral-scan wlan1 duration=30 range=5500-5700 save-file-name=test.txt

Now I’ve the file, but how to read it?

The text is unreadable.
I need some external software?

Thx
Mirko

Reply of Mikrotik Support

File save feature was created, but we have not created software to read these
files. Currently you can’t do anything with it.

:frowning: :frowning:

Mikrotik!

It has been one year. Any chance some software will be released? It is really quite annoying that I must plug into the device locally in order to monitor changes to spectrum usage that are affecting link performance.

It would be very helpful!

Can you publish structure of spectral-scan file?

How about a CLI command to just display these results?

Know its old, but there does not seem to be any updates so far, so i thought i would contribute my workaround.
Here is a site that will convert the spectral output file to a csv file.

http://chr-2.martinpetermadsen.com/Merlin/Publictools/Preproduction/ROSSpectrumScan

Update: I was sent a few files that failed to be resolved using the tool, there seems to be some variation in the formatting of the scan file.
As a result i have updated the tool to be more robust. If a file failed before November 8th, 2016 try once more.

This is obvs very old now and no longer supported on most devices. But here is a PHP parser for the spectral scan file:

$filePath		= "/tmp/mySpectral.scan";
$handle			= fopen($filePath, "rb");
$fsize			= filesize($filePath);
$fileContent	= fread($handle, $fsize);
fclose($handle);
		
$sweepDetail			= array();
$sweepDetail["scans"]	= 0;
$sweepDetail["data"]	= array();

$scanStrs	= array();
$byteStr	= bin2hex($fileContent);
$searchStr	= "8800000f00008800000e00008800000d00008800000";
$sPos		= strpos($byteStr, $searchStr);
if ($sPos !== false) {
	$headLen	= ($sPos + strlen($searchStr));
	$headStr	= substr($byteStr, 0, $headLen);
	$headRex	= "([0-9a-fA-F]{".$sPos."}" . $searchStr . "[0-9a-fA-F]+?)";
	preg_match_all("/".$headRex."/", $byteStr, $matches);
	
	$matchCount	= count($matches[1]);
	if ($matchCount > 0) {
		$origStr	= $byteStr;
		$headers	= array_reverse($matches[1]);
		
		foreach ($headers as $header) {
			$sStart	= strrpos($origStr, $header);
			if ($sStart !== null) {
				$scanStr	= substr($origStr, $sStart);
				$origStr	= substr($origStr, 0, $sStart);
				$scanStrs[]	= $scanStr;
			}
		}
		$scanStrs	= array_reverse($scanStrs);
	}
}

if (count($scanStrs) > 0) {
	
	foreach ($scanStrs as $sId => $sweep) {
		
		$bytes			= str_split($sweep, 2);
		
		if ($sId == 0) {
			
			//first sweep holds some extra bytes that no other sweep has. I have only seen this value: 00110105
			//$extraBytes	= array_slice($bytes, 31, 4);
			unset($bytes[31]);
			unset($bytes[32]);
			unset($bytes[33]);
			unset($bytes[34]);
			$bytes		= array_values($bytes);
			
			
			//the real first channel scanned is in 3 bytes.
			//it seems byte 34 is an offset and 35 + 36 is a band
			$sFreq		= (hexdec($bytes[34]) + hexdec($bytes[35] . $bytes[36]));
			$eFreq		= (hexdec($bytes[42]) + hexdec($bytes[43] . $bytes[44]));
			
			//find out at what byte the scan data starts, is interrupted and ends
			$fsScan	= null;
			$feScan	= null;
			$lsScan	= null;
			$leScan	= null;
			foreach ($bytes as $bId => $byte) {
				if ($byte == "7f") {
					if ($fsScan === null) {
						$fsScan	= $bId;
					} elseif ($feScan !== null && $lsScan === null) {
						//first byte of the second scan set
						$lsScan	= $bId;
					}
				} else {
					if ($fsScan !== null && $feScan === null) {
						//previous was the last byte of the first scan set
						$feScan	= ($bId - 1);
					} elseif ($lsScan !== null && $leScan === null) {
						//previous was the last byte of the second scan set
						$leScan	= ($bId - 1);
						break;
					}
				}
			}
		}
		
		if ($sId < 255) {
			$sweepId	= $bytes[50];
		} else {
			//3 additional bytes are added to accomodate the large sweep count
			$sweepId	= $bytes[51] . $bytes[50];
			
			if ($sId == 255) {
				//this moves the scan data as well
				$fsScan	+= 3;
				$feScan	+= 3;
				$lsScan	+= 3;
				$leScan	+= 3;
			}
		}
		
		//the first sweep is always empty, so we start at sweep 1
		if ($sId > 0) {
			$sweepDetail["scans"]++;
			
			//each sweep includes 2 sets of data, i have no idea what the second set displays
			//$dps2	= array_slice($bytes, $lsScan, ($leScan - $lsScan + 1));
			
			//however the first set of data mirrors about every 2nd output to the terminal.
			//the reason could be the terminal cannot keepup with the scanner
			
			//we do not get the same amount of dps for each frequency
			//the pattern is as follows
			//initial frequency has 3 dps, the second has 3, the third has 2 dps.
			//after this its 4 frequencies with 3 dps and 1 with 2dps, this continues to the end
			//i.e. 3-3-2-3-3-3-3-2-3-3-3-3-2-3-3-3-3-2...... etc
			//it is confirmed that buckets have no effect on this
			
			//there is also times when the last frequency does not have 3 values
			//i.e. if there are 114 data points, that means 8 to include the first 2dp freq
			//then 7 x (3+3+3+3+2) = 98 for the groups. That is 106, 3+3 gets us to 112, leaving only 2 dps
			//for the very last frequency, so it looks like it should have 3, but only has 2.
			//thats just fine
			$dps1		= array_slice($bytes, $fsScan, ($feScan - $fsScan + 1));
			$curFreq	= $sFreq;
			$i=0;
			$t=0;
			$c=0;
			foreach ($dps1 as $dpId => $val) {
				
				$decVal	= hexdec($val);
				$dbVal	= (256 - $decVal) * -1;
				
				if ($i == 3) {
					//second frequency dp
					$curFreq++;
				} elseif ($i == 6) {
					//third frequency with only 2 values
					$curFreq++;
				} elseif ($i == 8) {
					//now starting the 3-3-3-3-2 pattern
					$curFreq++;
					$t	= 0;
					$c	= 1;
				} elseif ($i > 8) {
					
					if ($t == 3 && $c < 4) {
						//next frequency with 3 values
						$curFreq++;
						$c++;
						$t	= 0;
					} elseif ($t == 3 && $c == 4) {
						//the next value is part of a frequency with only 2 values
						$curFreq++;
						$c++;
						$t	= 0;
						
					} elseif ($t == 2 && $c == 5) {
						//the next value is part of a frequency with 3 values
						//new round of values starting the 3-3-3-3-2 pattern
						$curFreq++;
						$t	= 0;
						$c	= 1;
					}
				}
				
				if (isset($sweepDetail["data"][$curFreq]) === false) {
					//frequencies should be in Hz
					$sweepDetail["data"][$curFreq]["frequency"]	= $curFreq * 1000000;
					$sweepDetail["data"][$curFreq]["stats"]		= array();
					$sweepDetail["data"][$curFreq]["raw"]		= array();
				}
				
				$sweepDetail["data"][$curFreq]["raw"][]	= $dbVal;
				$i++;
				$t++;
			}
		}
	}
	
	//create stats
	$sweepDetail["data"]	= array_values($sweepDetail["data"]);
	foreach ($sweepDetail["data"] as $dId => $fDatas) {
		
		$rawCount			= count($fDatas["raw"]);
		$stats				= array();
		$stats["dpCount"]	= $rawCount;
		$stats["max"]		= max($fDatas["raw"]);
		$stats["mean"]		= round(array_sum($fDatas["raw"]) / $rawCount, 2);
		$stats["min"]		= min($fDatas["raw"]);
		
		$sweepDetail["data"][$dId]["stats"]		= $stats;
	}
}

echo print_r($sweepDetail, true);