<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
<html>
<head>
<title>Help</title>
<style type="text/css">
h1, h2, h3, h4 { padding:0px; margin:10px; }
p { padding:0px; margin:5px; }
img { margin: 5px; }
</style>
</head>
<body>
<h2>Tenmon help</h2>

<p>Tenmon is intended primarily for viewing astro photos and images. It supports the following formats:</p>
<ul>
    <li>FITS 8, 16, 32 bit integer and 32, 64 bit float</li>
    <li>XISF 8, 16, 32 bit integer and 32, 64 bit float</li>
    <li>JPEG, PNG, BMP, GIF, PBM, PGM, PPM and SVG images</li>
    <li>CR2, CR3, NEF, DNG raw images</li>
</ul>

<h3>Main windows</h3>
<p>The main window shows the currently loaded image. On the left is the <i>Image info</i> panel which displays details about the loaded image.
The <i>File system</i> panel shows other images in the same directory as the loaded image. At the top is the main menu and below that is the
<i>Stretch panel</i> containing various options for auto-stretching linear image data.</p>
<p>All panels in the interface can be moved around and/or closed. Any closed or non-visible panel can be
re-opened through the <i>Docks</i> menu at the top.</p>
<p>At bottom there is status bar that show current lightness or red, green, blue pixel value under mouse cursor then X and Y coordinates and
if image contain World Coordinate System metadata it show celestial coordinates.</p>

<h3>Opening and saving images</h3>
<p>To load an image select <i>File->Open</i> and choose the file. After a file is loaded, it becomes visible in the main image panel, and the
<i>File system</i> panel will show the other images in the same directory.</p>
<p>The loaded image can be exported to a different format with <i>File->Save as</i>. Any of the formats JPEG, PNG FITS and XISF can be selected.
In the case of saving JPEG or PNG, the stretch function is applied to the saved image. FITS and XISF are saved/converted without applying the stretch.
To open an image, you can also drag and drop it to main window.</p>

<h3>View</h3>
<p>The <i>View</i> menu has options to control the size and scale of displayed images:</p>
<ul>
    <li><i>Zoom In</i> and <i>Zoom Out</i> magnify and shrink the image. The mouse wheel can be also used to zoom freely.</li>
    <li><i>Best fit</i> auto-zooms the image to fit the current size of the window.</li>
    <li><i>100%</i> will zoom to 1:1 scale.</li>
    <li><i>Bayer mask</i> set which bayer mask should be used when doing demosaicing.</li>
    <li><i>Colormap</i> select color pallette when showing image with false colours.</li>
    <li><i>Fullscreen</i> enlarges the main window to the whole screen.</li>
    <li><i>Thumbnails</i> will display small thumbnails for all images in the current directory.</li>
    <li><i>Slideshow</i> start showing all images periodically with interval that can be set in settings.</li>
</ul>
<p>Colormap can also be user defined. Place image file named colormap.png into application data directory.
On Windows"C:/Users/&lt;USER&gt;/AppData/Roaming/nou/Tenmon"  Linux: "~/.local/share/nou/Tenmon/" MacOS: "~/Library/Application Support/nou/Tenmon/"
This image should be 256 pixel wide. Each row of image will be used as separate color map and added to Colormap menu.</p>


<h3>Stretch toolbar</h3>
<p>This panel changes how images are displayed.
<br><img src=":/about/stretch-panel.png" alt="Stretch panel"></p>
<p>Starting on the left, there is slider scale with three adjustable points to manually control the stretch.</p>
<ul>
    <li>black point - all pixels with lower value (darker) than this setting will be clipped black</li>
    <li>mid point - defines the value to be stretched to 50% intensity</li>
    <li>white point - all pixels with higher value (brighter) than this will be clipped white</li>
</ul>
<p>Following the slider are 8 buttons to control image display:</p>
<ul>
    <li><i>Linked channels</i> toggle stretching each RGB channel individually.</li>
    <li><i>Auto Stretch</i> automatically apply black and mid points to render the image with optimal brightness.</li>
    <li><i>Reset</i> reset three values for black, mid and white point to default.</li>
    <li><i>Invert</i> invert colors to display the image as negative.</li>
    <li><i>False colors</i> show black and white in false colour palette.</li>
    <li><i>Debayer CFA</i> Demosaicing black and white image from CFA sensor to color one.</li>
    <li><i>Apply Auto stretch on load</i> toggle automatically applying Autostretch for each image when loaded.</li>
    <li><i>Draw equatorial grid</i> toggle drawing equatorial coordinate grid and catalogue objects. Needs that file have WCS data.</li>
</ul>

<h3>Marking images</h3>
<p>Images can be marked in the <i>Select</i> menu. To show a list of only the marked images, use <i>Select->Show marked</i>.
This dialog can be useful to clear marks from images. Marked images show a <b>*</b> character in the title bar of the main window.
Marked images can be copied or moved to a selected directory with <i>File->Copy/Move marked files</i>.
After copying or moving, the list of marked files is cleared. The list of marked files will be remembered after quitting the program.</p>
<p>Another way to mark images is in database view where you can select rows and then select mark or unmark action in context menu. Marked
files will be shown with bold text. Third way to mark files is from thumbnails view where you can press press <i>Shift</i> and click with left
mouse button and drag across thumbnails to mark them. Holding <i>Ctrl</i> will unmark files.</p>

<h3>File system and tree</h3>
<p>File system panel contain list of images in current opened directory. You can select file from this list and it will be displayed. It is also possible to
use arrow keys to go back (left and up) and forth (right and down) between images.</p>
<p>File tree show file system structure. You can right click to show context menu to perform various actions from <i>File</i> menu. There are also few others</p>
<ul>
    <li><i>Set as root directory</i> show only this directory and subdirectories</li>
    <li><i>Reset root directory</i> show whole file system</li>
    <li><i>Go up</i> show directory that is one level above current root directory</li>
</ul>

<h3>Database of FITS/XISF files</h3>
<p>Tenmon can scan a directory of FITS/XISF files and index metadata from FITS headers into it's internal database. This allows searching and sorting images based on that metadata.</p>
<p>To populate the database, select a directory of FITS/XISF files with <i>File->Index directory</i>.
After the selected directory is searched, metadata parsed from the images will be stored in the database.
To refresh the database, run <i>File->Reindex</i>. This will update any changed metadata and remove any record of
deleted files. To index new files, simply run <i>Index directory</i> again.</p>
<p>The database is viewed through a panel which is not visible in the default layout.  To add the database panel to the view,
toggle <i>Docks->FITS/XISF database</i>. Once visible, database panel shows the database as a table with a column for each property.
Below the table is button to select which columns/properties are displayed.</p>

<p>Also at the bottom of the database panel are three combo boxes and text inputs used for filtering.
Select the property to filter on with the combo box and in the adjacent text box enter a string to search for in that property.
These three combo box contain list of all properties that are found during indexing except first five. First one set searching in file name.
Next two "RA pos" and "DEC pos" allow to filter out indexed images that contain point with entered RA/DEC coordinate. Expected format is three
number separated by space. In case of "DEC pos" it also accept +- sign. Omitting one or two last number is also valid. Some examples "02 12 32" "-12 43 12" "+45 32" "13".
So for RA it means hour, minutes and seconds while for DEC it is degrees, minutes and seconds.
Setting both "RA pos" and "DEC pos" can return images that doesn't contain entered point as it search against minimum and maximum RA/DEC coordinates that images contain.
"RA range" and "DEC range" filter out images which center coordinate is within entered range.
Pressing Enter or clicking on <i>Filter</i> button will filter out database record according to search parameter.

<p>Wildcards:</p>
<ul>
    <li><b>%</b> (percent) is a wildcard representing zero or more of any characters.</li>
    <li><b>_</b> (underscore) is a wildcard for exactly one of any character.</li>
    <li>Without wildcard characters, the exact string must match.</li>
</ul>
<p><img src=":/about/filter.png" alt="Filter"><br>
This example filters for files where: "Bias" is in the file name, the OBJECT property is "M_42" (where the underscore can be any single character), and the DATE property begins with "2022".
</p>

<h3>Database tree</h3>
<p>This is another view that show indexed database as tree. You can add or remove tree grouping that construct a tree structure from FITS keywords. Each level of tree
will be based on this grouping. You can specify one keywords multiple times. When adding a grouping you can also specify aggregation function that is applied to last level.
SUM will add up all numbers in that group usefull for example for EXPTIME. COUNT will show how many files are in that group. AVG will show average value, MIN,MAX,MEDIAN will calculate
minimum, maximum and media value in that group. So if you select CCD-TEMP as last with AVG it will calculate average temperature. Double click on file in tree view will open
that file.</p>

<h3>Plate Solving</h3>
<p>This module can plate solve images and update FITS header with solution for FITS and XISF images.
<b>Profile</b> this set various parameters that affect star extraction and solving.
<b>Starting point</b> program will try to automatically determine optimal starting point which helps to speed up solving.
You can leave one or both unchecked then it will attempt to do blind solving. If the position or scale is wrong it can actually
fail to solve.
<b>Solution</b> this section contain resulting solution like RA,DEC coordinates center of image, image field of view, orientation as degrees E of N,
image scale in arcseconds per pixel, number of stars extracted and HFR fitting and eccentricity. Then there is log window for debug information from
solver.
</p>
<p>Then finally there are various action button. Settings button show dialog where you can set path to existing index files or auto download some.
Extract button will just extract stars from image and it will show their count, HFR and eccentricity. This action doesn't need index files.
Solve button will try to find coordinates of images. Abort button will stop extraction or solving. Update FITS header will update FITS fits keywords
with found solution.</p>

<p>In settings dialog you can set path to index files which is by default custom internal one. It also try to locate commonly used path from other
programs like KStars for astrometry.net index files.
</p>

<h3>File Manager</h3>
<p>
This is simple double panel file manager. It can show columns with selected FITS keywords. Each panel have tabs where it easily switch between
multiple directories. You can copy or move files and directories either inside one panel or between two panels by selecting and then dragging.
By default files are copies. To move then press Shift key before start dragging. Double click on file will open it in main window if it is image
or other file it will open default program that is associated with it this file type. You can also drag file to other programs like from default
file explorer programs that are in system or from file explorer to this program.
</p>
<p>
In menu you can select which FITS keywords will be showed. Temporarilly disable loading FITS header or copy file paths of selected files as text.
</p>

<h3>Command line options</h3>
<p>
Tenmon can be executed from command line. It support these command line options.
<ul>
<li><i>--thumb, --thumbnail &lt;path&gt;</i> Generate thumbnail and save it to path. It generate it from first file provided as argument.</li>
<li><i>-s, --size <size></i> size of generated thumbnail. Aspect ratio of input image is preserved.</li>
<li><i>--script &lt;script&gt;</i> execute a script from file path same manner as executed from GUI.</li>
<li><i>--scriptarg &lt;arg&gt>;</i> pass this string as variable scriptarg to a running script.</li>
<li><i>--outdir &lt;dir&gt;</i> output dir for script execution. By default current working directory is used.</li>
<li><i>--noexit</i> by default application exit when execution of script specified with --script ends. This prefent that.</li>
<li><i>-h, --help</i> show help end exit.</li>
Any other arguments are taken as input paths. If only one file path is specified then that image is open and image list is populated by directory
containing that image. If directory is specified then it is same as selecting that directory in "Open directory recusivelly". If multiple files are
specified then image list will contain just these speicified images.

When exuecting script with --script then these paths are used as input files and directories as in "Batch processing"
</ul>
</p>

<h3>Batch processing</h3>
<p>This module allow to write scripts in JavaScript that process image files. Batch Processing window consist from three main parts. On top is list of input files and directories.
You can add directories or individual files to this list. Directories are scanned recursively to find all files even non image files. This list of files is then passed to script in array named <b>files</b>.
In script you can then iterate through files like this.</p>
<pre>for(file of files)
{
    if(file.suffix() == "fits")
    {
        core.log(file.fileName());
        file.convert(file.relativeFilePath(), "XISF");
    }
}
</pre>
<p>Bellow this list is output directory. All relative paths passed as arguments into methods like copy(), move() or convert() will be relative to this output directory. If you pass absolute path to methods then
this output directory is ignored.</p>

<p>Bellow that is list of scripts. These scripts are located in application data directory. Select script which you want to run by clicking on it. Clicking on <i>Open scripts</i> will open directory with these scripts where you create new and edit them.
Location of this directory is on Windows: "C:/Users/&lt;USER&gt;/AppData/Roaming/nou/Tenmon/scripts"  Linux: "~/.local/share/nou/Tenmon/scripts" MacOS: "~/Library/Application Support/nou/Tenmon/scripts"</p>

<p>Next is Log windows that contain any messages that come from scripts. Mainly calls to <code>core.log()</code> At bottom there is console that enable run simple script commands and
buttons that can start or stop execution of selected scripts.</p>

<h4>core</h4>
<p>There is global object called <b>core</b> that have these methods.</p>
<ul>
<li><b>log(message)</b> print message to log window.</li>
<li><b>mark(file)</b> mark file same way as in GUI. Takes object of type <i>File</i> as argument.</li>
<li><b>unmark(file)</b>  unmark file same was as in GUI. Takes object of type <i>File</i> as argument.</li>
<li><b>isMarked(file)</b> check if file was marked.  Takes object of type <i>File</i> as argument.</li>
<li><b>setMaxThread(maxthread)</b> set maximum number of concurrent thread when doing asynchronous task.</li>
<li><b>sync()</b> wait until all asynchronous tasks are done.</li>
<li><b>getString(label = "", text = "")</b> show dialog box to get string value from user. String value passed in first argument is used as description label. Second argument text is default value in text box.
        Both parameters are optional so calling just <i>getString()</i> is valid. When cancel is pressed it return Undefined.</li>
<li><b>getInt(label = "", value = 0)</b> show dialog box with input box to retrieve integer value. String value passed in first argument is used as description label.
        Second parameter is default value in input box. Both parameters are optional. When cancel is pressed it return Undefined.</li>
<li><b>getFloat(label = "", value = 0, decimals = 3)</b> show dialog box with input box to retrieve decimal value. String value passed in first argument is used as description label.
        Second parameter is default value in input box. All three parameters are optional. When cancel is pressed it return Undefined.</li>
<li><b>getItem(items)</b> show selection dialog which allow to select one item from array of items. It return selected item as string. When cancel is pressed it return Undefined.</li>
<li><b>setStartingSolution(solution)</b> with this you can set starting point and image scale. It accepth object with attributes "ra", "dec", "pixscale".
        Same object as returned by <i>File.solve()</i> method. You can also call it without paramer in which case it will clear any previously set values.</li>
<li><b>getSolverProfile()</b> return solver profile as Object.
<pre>var profile = core.getSolverProfile();
core.log(JSON.stringify(profile));</pre></li>
<li><b>setSolverProfile(index)</b> set solver profile by index. Valid values are from 1 to 8 as in GUI.</li>
<li><b>setSolverProfile(profile)</b> set solver profile. Parameter is same as object returned by <i>getSolverProfile()</i> method</li>
<li><b>question(question, buttons, title = "")</b> show dialog with question. First argument <i>question</i> is string. Second argument <i>buttons</i>
    is array of following strings. "ok", "yes", "no", "yesall", "noall", "abort", "retry", "ignore", "cancel", "discard", "apply", "reset" Third argument <i>title</i> is optional string that show in title bar.
    It return button that was clicked as a string.
    <pre>var button = core.question("Apply to all files?", ["yes", "no"]);</pre>
    </li>
<li><b>plot(graph)</b> this method show graph defined by JS object.

<pre>
var chart = {
    "title": "Chart title", // Title that will show on top of chart
    "legend":
    {
        "visible": true,// default is true
        "align": "left" // allowed values are "top", "right", "bottom", "left". default is "top"
    },
    "series":[ // array of data series
        {
            "title": "HFR",
            "type": "bar", // type of the serie. Can be one of "line", "points", "linePoints", "bar"
            "y":[2.5,3.1,2.6,2.2] // array of values
        },
        {
            "title": "Ecc",
            "y":[0.37, 0.4, 0.35, 0.25],
            "color": "red" // color of serie. It can be name of color like "green" or RGB hex value "#ccddff"
        },
        {
            "title": "Stars",
            "type": "points", // type of serie. can be "line", "points", "linePoints" and "bar". Default is "line"
            "shape": "star", // shape of markers. valid only for points
            "x":[1, 2.5, 3.5, 6],// line, points and linePoints can also have "x" values. Otherwise it assume sequence 0,1,2 ...
            "y":[523,412,487,510],
            "y2": true, // if set to true this serie will use secondary Y axis
            "bestFit": true, // show best fit line
            "color": "#0000ff"
        }
    ]
};

core.plot(chart);
</pre>
</li>
</ul>

<h4>File</h4>
<p>In <b>files</b> array there are instances of type <b>File</b> objects that have these methods.</p>
<ul>
<li><b>fileName()</b> returns the name of the file, excluding the path.</li>
<li><b>absoluteFilePath()</b> returns an absolute path including the file name.</li>
<li><b>absolutePath()</b> returns an absolute path without the file name</li>
<li><b>relativeFilePath()</b> return relative path including file name relative to directory that was in list of directories to be scanned. For example you add C:/images as input directory. In this directory there
        is file <i>C:/images/lights/red/M42_001.fits</i> then this method will return <i>lights/red/M42_001.fits</i></li>
<li><b>relativePath()</b> return same path as previous method just without file name. <i>lights/red</i></li>
<li><b>baseName()</b> return file name up to the first dot. For example for <i>some.file.name.fits</i> it will return <i>some</i></li>
<li><b>completeBaseName()</b> return file name up to the last dot. For example for <i>some.file.name.fits</i> it will return <i>some.file.name</i></li>
<li><b>suffix()</b> return string after last dot in file name. For example <i>fits</i></li>
<li><b>size()</b> return size of file in bytes.</li>
<li><b>fitsKeywords()</b> return array of strings with every keyword that is in header. <i>SIMPLE,BITPIX,NAXIS,NAXIS1,NAXIS2,EXTEND,COMMENT</i></li>
<li><b>fitsValue(key)</b> return value for keyword. In case that there is multiple occurrences it return last one.</li>
<li><b>fitsValues(key)</b> return array of values for keyword.</li>
<li><b>fitsRecords()</b> return array of objects with properties <b>key, value</b> and <b>comment</b> </li>
<li><b>modifyFITSRecords(FITSRecordModify)</b> modify FITS header by adding, removing or updating FITS record. Return true on success. Refer to <i>FITSRecordModify</i></li>
<li><b>isMarked()</b> return true if file is marked.</li>
<li><b>copy(newpath)</b> copy file to new location. It return instance of new <i>File</i> object that represent this copied file. This path can be relative or absolute. In case that <i>newpath</i> parameter is relative
        path then it "Output directory" from GUI windows is used as base directory. Parameter <i>newpath</i> can absolute path. File is then copied to this path. In case that copy fail it return null.</li>
<li><b>move(newpath)</b> move file to new location. It return false if move failed. This can happend if destination is not writable but also if destination file already exist. This functions does not overwrite existing file.
        This path can be relative or absolute. In case that <i>newpath</i> parameter is relative path then it "Output directory" from GUI windows is used as base directory. Parameter <i>newpath</i> can be absolute path.
        File is then moved to this path.</li>
<li><b>convert(outpath, format, params)</b> convert image file from any format that program is able to open into FITS, XISF, JPEG, PNG, BMP.
        Parameters are: <i>outputpath</i> path where converted image will be saved. It automatically replace suffix according to format. This method return new instance of <i>File</i> that point to converted file.
        <i>format</i> one of "FITS" "XISF", "JPG", "PNG", "TIFF" or "BMP".
        <i>params</i> object with attributes
        <ul>
        <li>"compressionLevel" used with XISF format. integer value between 0-100 determining speed and compression ratio.</li>
        <li>"compressionType" for FITS format it can be "gzip" and "rice". For XISF it can be "zlib", "lz4", "lz4hc", "zstd", "zlib+sh", "lz4+sh", "lz4hc+sh", "zstd+sh"</li>
        <li>"binning" any integer value above 1 will perform integer downsample</li>
        <li>"average" by default set to true. If you set to false it will sum pixel values instead of averaging when performing binning.</li>
        <li>"resize" downsample image to defined width and height by subobject <code>"resize":{"width": 128, "height": 128, "aspect":"keep"}</code>
        "aspect" determine how to handle aspect ration when resizing image. "keep" and "expand" preserve original aspect ratio. Difference is that "keep" resulting image will be at most requested size
        "epand" resulting size will be at least requested size. For example input image 800x600 pixels and resizing to 128x128. With keep resulting image will be 128x96 while with epxand it will be 170x128.
        If set to "ignore" then resulting image will be exact size 128x128 ignoring original aspect ratio. By default keep is used.</li>
        <li>"autostretch" when set to true it apply automatic stretch function to pixel values. By default it is set to false.</li>
        </ul>
        In case that both binning and resizing is set binning is performed first then resing.
<pre>file.convert("converted_file.xisf", "xisf", {"compressionType": "zstd+sh", "compressionLevel": 70});
file.convert("converted_file.fits", "fits", {"compressionType": "rice"});
file.convert("converted_file.jpg", "png");
file.convert("thumbnail.jpg", "jpg", {"binning": 2, "average": true, "resize": {"width":256, "height": 256, "aspect":"ignore"}, "autostretch": true});</pre></li>
<li><b>convertAsync(outpath, format, params)</b> same as previous method but it does conversion in separated thread asynchronously and in parallel.
    Before calling any method on object returned by this method you must call <code>core.sync();</code> to ensure that conversion is done and destination file exists.
    <pre>let compression = {"compressionType": "zstd+sh"};
let convertedFiles = [];
for(file in files)
{
    if(file.suffix() == "fits")
        convertedFiles.push(file.convertAsync("xisf/" + file.fileName(), "XISF", compression));
}
core.sync(); // ensure that files exist
for(file of convertedFiles)// now we can iterate over the files
{
    core.log(file.fileName() + " " + file.size()); // let print compressed file sizes
}</pre></li>
<li><b>stats()</b> calculate basic images statistics and return them as object with attributes "mean", "stddev", "median", "min", "max" and "mad".
<pre>let s = file.stats();
core.log("Median value is " + s.median);</pre></li>
<li><b>solve(updateHeader)</b> this method will run plate solving on this image and will return solution in form of object with these attributes "ra" and "dec" which are center coordinates of image
        "fieldWidth" and "fieldHeight" which is FOV of image in arcseconds, "orientation" is degrees east of north, "pixscale" scale of image in arcseconds per pixel,
        "parity" true false value if the image was flipped in vertical direction, "raError" and "decError" deviation from starting point.
        When updateHeader is set to true it update FITS header for file with this solution. Default value is false.</li>
<li><b>extractStars(hfr)</b> extract stars will run extraction of stars. When parameter hfr is set to true it will fit HFR on every star.
    It return array of objects representing extracted stars. Each object in array
    will have these attributes "x" and "y" pixel coordinates of center of star, "mag" relative magnitude of star, "flux" total flux, "peak" peak value of star, "HFR" half flux radius of star,
    "a" and "b" semi major and minor axis of star, "theta" angle of orientation of the star, "ra" and "dec" coordinates of star, "numPixels" number of pixel occupied by the star in image.</li>
</ul>

<h4>FITSRecordModify</h4>
<p>This class is used to define modify operation FITS header in FITS and XISF files. It can remove update and add records. Order of operation is also remove then update and last add.
The keyword names may be up to 8 characters long and can only contain uppercase letters, the digits 0-9, the hyphen, and the underscore character.</p>
<pre>let modify = new FITSRecordModify();
modify.updateKeyword("OBJECT", "M42");
modify.updateKeyword("MYTILE", "PART1", "adding custom keyword so WBPP can group it");
modify.removeKeyword("OBJECT");
// doesn't matter that it is specified as last. This will first remove
// existing OBJECT record and then add again OBJECT=M42
for(file in files)
{
    file.modifyFITSRecords(modify);
}</pre>

<ul>
<li><b>new FITSRecordModify()</b> create new instance of object.</li>
<li><b>removeKeyword(key);</b> specify removing of record with <i>key</i> as keyword.</li>
<li><b>updateKeyword(key, value, comment = "")</b> specify updating existing keyword with value and comment. Comment is optional parameter. If record with keyword doesn't exist then it will add new one.
        Unless you want to have multiple records with same keyword (for example HISTORY) always use this method and not addKeyword.</li>
<li><b>addKeyword(key, value, comment = "")</b> specify adding new keyword</li>
</ul>

<p><small>PS: Kanji in icon means astronomy in Japanese</small></p>
</body>
</html>
