% \iffalse meta-comment
%
% $Id: luaaddplot.dtx 6 2024-09-22 00:02:45Z reinhard $
%
%<*driver> 
\documentclass[a4paper,11pt]{ltxdoc}
\MakeShortVerb{\|}

\advance\textheight by 2\baselineskip 
\usepackage[english]{babel}
\usepackage{tgpagella}
\usepackage[dvipsnames]{xcolor}
\usepackage{parskip}
\usepackage{siunitx}
\usepackage[colorlinks,bookmarksopen,bookmarksnumbered]{hyperref}
\hypersetup{linkcolor=BrickRed}
 
\author{Reinhard Kotucha}
\title{\textsf{luaaddplot}}
\date{Version 1.0a\\[.5ex]\today}
\begin{document}
\maketitle
\vspace*{1cm}

\begin{abstract}
  \parindent=0pt \parskip=1ex
  \noindent The \textsf{pgfplots} package supports plotting data from files.
  If these files are generated by measuring devices they almost always
  have to be pre-processed.
  
  This packge provides a macro |\luaaddplot| which extends the
  functionality of |\addplot| by an optional argument containing
  a $\lambda$- expression which is evaluated for each line of the data
  file.

  The package requires Lua\TeX\ or Lua\LaTeX.
\end{abstract}
\vspace*{1cm}
\tableofcontents
\vspace*{\fill}

\newpage

\section{Introduction}
When plotting files generated by measuring devices the data files
often have to be pre-processed before they can be passed to \textsf{pgfplots}.  
In most cases a header describing the device configuration must be
removed, column delimiters have to be changed, and units have to be
scaled (\si{\hertz} to \si{\mega\hertz}, \si{\volt} to
\si{\micro\volt}, etc\ldots).  In some cases more complex operations
are required.

\subsection{Usage}

\verb|  \luaaddplot|\oarg{options}| file "|%
$\langle$\textit{filename}$\rangle$|"|[|, |$\langle$\textit{$\lambda$-expression}$\rangle$]|;|

Everything between \cs{luaaddplot} and the keyword |file| is passed to
\cs{addplot}, everything between |file| and the semicolon is
processed by Lua and valid Lua syntax is required here.  You can
consider code between |file| and the terminating semicolon as
arguments of a Lua function with braces omitted. 

\subsection{Reading the data file}

Data files generated by measuring devices usually have an ASCII
header describing the settings. These lines are often preceded by
comment characters but not always.  In order to avoid pre-processing
with another tool, all lines which do not begin with a number are
ignored.

Different devices use different column delimiters.  Therefore tabs,
colons, semicolons, and commas are replaced by a space.  With other
words, various datafile formats are converted to a to a \textsc{Matlab}
compatible format.

\subsection{\texorpdfstring{$\lambda$}{lambda}-Expressions}

A $\lambda$-expression is an anonymous function and looks like this: 
\begin{verbatim}
  function (a) 
    return a[1], a[2] 
  end
\end{verbatim}

The formal parameter |a| (its name can be chosen arbitrarily) is
replaced by an ordered list of columns for each line in the data file.
The return values are the $x$ and $y$ values to be plotted and are
passed to \cs{addplot}.  Always two values must be returned.  Please
note that the index of the first column is 1 in Lua and not 0, as in
many other programming languages.

In the example above the first and second column of the data file
will be plotted.  This is the default if the $\lambda$-expression is
omitted. 

\newpage

\section{Examples}

\subsection{Scaling}

The following is an excerpt of a data file generated by a spectrum analyzer.

\begin{verbatim}
x-Unit;Hz;
y-Unit;dBm;
Preamplifier;OFF;
Transducer;OFF;
Values;501;
0;43.660163879394531;
6000000;-53.616184234619141;
12000000;-60.31707763671875;
18000000;-62.548038482666016;
54000000;-66.722061157226563;
\end{verbatim}

Frequencies are in \si{\hertz} but for the plot we prefer
\si{\mega\hertz}.  The solution is
\begin{verbatim}
\luaaddplot[blue] file "spectrum.data", function (col) 
                                          return col[1]/1e6, col[2]
                                        end;
\end{verbatim}


\subsection{Processing}

The following is an excerpt of a data file generated by a vector
network analyzer.

\begin{verbatim}
%% Date: 2021-02-25 11:36:04
%% Data & Calibration Information:
%% Trc1:S11(Full One Port)
%%freq[Hz] re:Trc1_S11 im:Trc1_S11 
6.650000000000000E+008,3.684957681171731E-001,-9.205383204111828E-001,
6.652500000000000E+008,3.717039523633851E-001,-9.215835251874305E-001,
6.655000000000000E+008,3.687706454916999E-001,-9.207422307051146E-001,
6.657500000000000E+008,3.574212650433284E-001,-9.176705980253278E-001,
6.660000000000000E+008,3.453552411257967E-001,-9.254690887689956E-001,
\end{verbatim}

The first column denotes the frequency in \si{\hertz}, the second and
third column denote the real and imaginary part of the impedance
$s_{11}$ respectively.

Now we want to plot
\begin{displaymath}
  20 \log_{10} \sqrt{(\text{Re\,} s_{11})^2 + (\text{Im\,} s_{11})^2} 
\end{displaymath}
with frequencies in \si{\mega\hertz}. 

The solution is
\begin{verbatim}
\luaaddplot[green] file "s11.data",
        function (col) 
           return col[1]/1e6, 20*math.log10(math.sqrt(col[2]^2 + col[3]^2))
        end;
\end{verbatim}


\newpage
\section{Implementation} 
\DocInput{luaaddplot.dtx}
\end{document}
%<*tex|sty|lua>
%</tex|sty|lua>

%</driver> 
% \fi
%    \begin{macrocode}
%<tex>%% File 'luaaddplot.tex', generated from luaaddplot.dtx'.
%<sty>%% File 'luaaddplot.sty', generated from luaaddplot.dtx'.
%<lua>-- File 'luaaddplot.lua', generated from luaaddplot.dtx'.
%<lua>--[[
%%
%% luaaddplot.dtx
%% Copyright 2022 Reinhard Kotucha <reinhard.kotucha@gmx.net>
%%
%% This work may be distributed and/or modified under the
%% conditions of the LaTeX Project Public License, either version 1.3
%% of this license or (at your option) any later version.
%% The latest version of this license is in
%%   http://www.latex-project.org/lppl.txt
%% and version 1.3 or later is part of all distributions of LaTeX
%% version 2005/12/01 or later.
%%
%% This work has the LPPL maintenance status `maintained'.
%% 
%% The Current Maintainer of this work is Reinhard Kotucha.
%%
%% This work consists of the files luaaddplot.dtx and luaaddplot.ins
%% and the derived files luaaddplot.tex, luaaddplot.sty, and luaaddplot.lua.
%<lua>--]]
%    \end{macrocode}
% \subsection{luaaddplot.tex}
%    \begin{macrocode}
%<*tex>
\directlua{require('luaaddplot')}
\def\luaaddplot#1file#2;{
        \directlua{luaaddplot.opts('#1')luaaddplot.readfile(#2)}}
%</tex> 
%    \end{macrocode}
% \subsection{luaaddplot.sty}
%    \begin{macrocode}
%<*sty> \ProvidesPackage{luaaddplot}[2024/09/22 v1.0a]
\input luaaddplot.tex
%</sty>
%    \end{macrocode}
% \subsection{luaaddplot.lua}
%    \begin{macrocode}
%<*lua>

module('luaaddplot', package.seeall)

%    \end{macrocode}
% \DescribeMacro{readfile()}
% Read and pre-process file \verb|file| and apply
% $\lambda$-expression.  If the second argument is \verb|nil| the
% first and second column is returned. 
%    \begin{macrocode}
function readfile (file, lambda)
%    \end{macrocode}
% Check whether datafile exists.
%    \begin{macrocode}
  if not lfs.isfile(file) then
    error('\nERROR: File "'..file..'" not found.')
  end

  local data = io.open(file)

  for line in data:lines() do
%    \end{macrocode}
% Remove all spaces at the beginning of a line.
%    \begin{macrocode}
    line = line:gsub('^%s+', '')
%    \end{macrocode}
% Ignore all lines which don't begin with a number.  All comments
% and empty lines in data files are ignored.
%    \begin{macrocode}
    if line:match('^%-?%.?[0-9]') then
%    \end{macrocode}
% Replace possible non-space delimiters by spaces.  This allows to
% process various datafile formats the same way as \textsc{Matlab}
% files without manual interaction.
%    \begin{macrocode}
      line = line:gsub('[\t:;,]', ' ')
%    \end{macrocode}
% The result must be a table with two columns.  If no $\lambda$
% expression is provided, the first two colums of the data file are
% returned.  With a $\lambda$ expression as second argument arbitrary
% columns can be selected from a data file and can be pre-processed in
% any way possible.
%    \begin{macrocode}
      local a, b
      local cols = line:explode (' +')
%    \end{macrocode}
% Convert strings to numbers.  Because $\lambda$ expressions can
% access any column we convert all table entries from strings
% to numbers, if possible, instead of only the return values.
% Numbers are returned to \TeX\ as floating point numbers.
%    \begin{macrocode}
      for i, col in ipairs(cols) do
        cols[i] = tonumber(cols[i])
      end

      if lambda then
        a, b = lambda(cols)
        if a and b then
          tex.print(string.format('%g %g', a, b))
        end
      else
        tex.print(string.format('%g %g', cols[1], cols[2]))
      end
    end
  end
  tex.print('};')
  data:close()
end
%    \end{macrocode}
% \DescribeMacro{opts()}
% Pass optional arguments from \verb|\luaaddplot| to \verb|\addplot|.
%    \begin{macrocode}

function opts (s)
  tex.print('\\addplot'..s..' table {')
end

% </lua>
%    \end{macrocode}

%\endinput
% Local Variables:
%  mode: LaTeX
%  TeX-master: t
%  TeX-engine: luatex
%  indent-tabs-mode: nil
%  coding: utf-8-unix
% End:
% vim:set tabstop=2 expandtab:
